Entries from 2009

Easy Deploys to Multiple Environments with Capistrano

At Viddler, we’re switching to using Git and Capistrano for our internal projects, and I was tasked with setting up the system to deploy our code to both staging and production environments. Capistrano doesn’t have built-in environment switching, but it’s dead simple to add it yourself.

The key to getting this to work is to utilize Capistrano’s ability to chain tasks–if you run cap task1 task2, it will run the two commands in the order you listed them. Variables are shared between the tasks, so if you set a variable in task1, task2 will be able to access it. To get multiple environments working, you just create tasks to set environment variables, which you call before the actual task you want to run.

The first step is to figure out which variables you need to change, and then move them into their own tasks. Here’s an example of the tasks we added:

desc "Run on staging server"
task :staging do
  server "staging.myserver.com", :app, :web, :db, :primary => true
end

desc "Run on production server"
task :production do
  server "myserver.com", :app, :web, :db, :primary => true
end

Now, if you want to deploy to staging, just run cap staging deploy, and for production run cap production deploy. Easy.

Posted on December 10, 2009 Leave a Comment
Tagged with: , , , ,

The Perfect Git Workflow for a One Person Project

A few months ago, I started investigating Git, and I fell in love with how much easier it made managing my code. I’m managing the source code to this site with Git, and along the way I’ve come up with a pretty good workflow for myself. The basic steps are:

  1. Branch
  2. Commit
  3. Rebase
  4. Merge
  5. Deploy

Let’s go through each one to see how it all works together.

Branch

With SVN, I found myself hating branching–it was always a complicated procedure, and I could never remember how to do it. In Git it’s as easy as git checkout -b <branch-name>, and you’re ready to go. Once you have a branch, you can modify it however you want, and you don’t have to worry about interfering with the master branch. In order to keep your master branch bug free, commit only to branches, not to master itself.

In addition to creating new branches for major features, I always have a few branches around that I pop into for certain things:

  • “design” - any changes I want to make to the design go here
  • “optimize” - optimizations to the site
  • “bugfixes” - a place to work on minor bugfixes

Having these branches allows me to make small fixes to the site, and if it turns out it’s more than just a small fix, it doesn’t interfere with anything else.

Commit

In the Subversion world, it’s a pretty common practice to make very large commits, consisting of many changes. With Git, you should constantly be committing. By making many commits, you make it easier to find bugs you may have introduced, and it makes it a lot easier to track your progress. If you don’t like the thought of wading through long lists of commits in your logs, don’t worry–before bringing it over to the master branch, you can consolidate things with interactive rebasing, but while you’re hacking away on a branch, it really is advantageous to have many small commits.

Rebase

Rebasing is one of the harder things to grasp when you’re first learning about Git. For in-depth coverage of the topic, check out the Rebasing page in the Git Community Book. In a nutshell, doing git rebase master takes any commits to master and inserts them into your current branch, so you can then make sure your new code still works, and it’s a lot less hazardous than doing a merge. Rebasing your branch before putting into master is really important because it allows you to deal with any merge issues before the code goes to your main branch. To rebase, just run git rebase master.

Merge

After rebasing the branch, it’s safe to merge it into master. Since I’ve already dealt with any merge issues with the previous step, it’s as simple as checking out the master branch and running git merge <branch-name>

Deploy

I use Capistrano to deploy my code, so I’m constantly typing git push followed by cap deploy to deploy changes to my server. To make it easier, I just put both commands into one git alias:

deploy = !git push && cap deploy

Now I just need to run git deploy and it automatically pushes all of my changes to the remote Git repository and then deploys the site using Capistrano. Here’s some more information about Git aliases.

Useful tools

Though I primarily use Git through the command line, I really like using GitX to visualize branches. To host my repositories on my server, I use Gitosis, though if I had a few more projects, I’d dole out the money for a paid account at GitHub.

Have a great Git workflow? Think mine’s terrible? Let me know in the comments!

Posted on July 30, 2009 4 Comments
Tagged with: , , , ,

4 Easy Frontend Optimizations for Your Site

Over the past few weeks, I’ve been tweaking the site a bit to help speed up page loads. After running YSlow and Page Speed, it seemed like the slowest aspects of my site weren’t necessarily happening behind the scenes with my Ruby code, but rather on the frontend with images, Javascript, and CSS. Below, I’ve listed four ways you can squeeze more speed out of your site without even touching backend code.

Gzip your files

One of the first things I did to speed up my site was to start using mod_deflate to gzip compress data from my site before it’s even sent to the viewer’s browser. Basically what happens is after my Rails app returns the HTML for the page, Apache then compresses it and sends the compressed version to the user. Enabling this brought my front page size down to 6KB from around 22KB, a 73% improvement! You should enable this on all of your plaintext files, such as external CSS and Javascript files, as well as any HTML that’s being sent to the browser–essentially as many files as you can get away with.

For more information, make sure to read Paul Stamatiou’s excellent post on mod_deflate, where he goes through the steps of gzipping your site on Apache.

Set a far future expires header for images, stylesheets, and JS

When your browser wants to fetch a file, it first checks to see if it’s stored in its local cache, and if it has a new enough version of the file, it will display that instead of taking the time to download it from the server. The problem is, how does your browser know if the cached version is “new enough”? The trick is to use the Expires header for any files you know aren’t going to change very often (such as images, stylesheets, and javascript). I did this on all of my photos, and it has made a huge difference–after turning on the header, my bandwidth usage went down by 70%, since browsers were no longer re-requesting images they already had.

For how to set a far future expires header with Apache, check out Christian Johansen’s tutorial on the subject.

Load Javascript Libraries from Google

Nowadays, just about every site is using a javascript library of some sort, whether it’s Prototype, jQuery, or something else. Since the library might not change from site to site, why not leverage that to cut down on download times? Google has done the work for you, with their AJAX Libraries API, which provides you with a CDN-hosted version (what is a CDN?) of popular JS libraries, so they’re super fast, and once your browser downloads the library for the first time, it will be retrieved from cache for any site that uses the same file.

There are 2 ways to use Google’s hosted libraries, via the API listed on their page or just by including the file. For instance, I’m including jQuery 1.3.1 with this code:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>

I’m not sure exactly how much faster this has made my site, but it definitely keeps bandwidth usage down and makes the user experience much quicker.

Defer Loading of Stat-tracking Javascript

I’ve always run two separate stat tracking scripts on this site: Google Analytics and Mint, but recently, I really started to notice they were slowing down page loading time (especially with Mint). To combat this, I decided to defer loading the scripts until after the page has finished, so as to ensure they don’t impact the user experience negatively. To do this, I just used jQuery’s $.getScript() function like this:

$(document).ready(function() {
  var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  $.getScript(gaJsHost + 'google-analytics.com/ga.js', function() {
    var pageTracker = _gat._getTracker("GOOGLE TRACKING CODE");
    pageTracker._initData();
    pageTracker._trackPageview();
  });
  $.getScript('http://path/to/mint/?js');
});

Just customize it to point at your stat tracking scripts, and you’re ready to go. One caveat with this–it may impact your stats, because scripts won’t be loaded until the page finishes rendering, meaning if someone clicks off the page before them, it might not register their visit. I haven’t had enough time to see if my decision to defer loading has affected my stats, but I’m guessing it would have at least a minor impact.

Anything else?

Is there anything else you’ve done for your site that’s really lowered your loading times? Let me know in the comments!

Posted on July 4, 2009 Leave a Comment
Tagged with: , , , , , , , ,

Sass Without the Hass(le)

For a recent project, I decided to try out Sass, a “meta-language on top of CSS,” which allows to do all sorts of neat things like use variables, do math, and have mixins. The only problem: since you’re not writing CSS, it has to be compiled whenever you want to view your page, which can be pretty annoying if you’re just working on a static template (which I was).

At first, I was using the standard command: sass input.sass output.css, but that became too tiresome, and I was struck with an idea–what if I just created a server solely for serving the compiled CSS file? Using Sinatra, I was able to make a dead easy way to use Sass while working on a static template. Follow along to see how you can do this yourself.

First, make sure you’ve got the haml and sinatra gems installed:

gem install haml sinatra

Now, in your development directory, create a file called app.rb and paste the following code:

require 'rubygems'
require 'sinatra'
require 'haml'

get '/style.css' do
  headers 'Content-Type' => 'text/css; charset=utf-8'
  sass :style
end

Now, create a views/ directory in that same folder, and drop a style.sass file in there. This is where you’ll be writing your Sass. Now, you need to start up your Sinatra server, and to do that, just run the following command:

ruby app.rb

Now, if you go to http://localhost:4567/style.css, you’ll see your compiled CSS, and every time you update your Sass file, the code is recompiled. Just change the CSS <link> tag in your HTML template to point to the style.css file, and you’re ready to go!

Posted on June 19, 2009 2 Comments
Tagged with: , , , , , ,

Improving on Related Entries

A little while back, I posted about how I was determining related entries for my site. That method worked, but once I redid my site and added my 250+ Flickr photos, it started to really slow down when finding related photos, because of the increase in tags and posts. The real issue was that I was doing most of the work in Ruby, when it really should have been done with SQL. So, I decided to rewrite it.

Note: If you haven’t looked at my previous entry on the subject, you might want to take a look at it, just for the general idea of what I’m trying to accomplish. Essentially, I’m trying to find related posts by comparing tags. Here’s my new Post#related code:

def related(limit=5)  
  return [] if tags.empty?

  join_array = tags.collect {|tag| "posts_tags.id = #{tag.id}"}

  tags_join = "AND (#{join_array.join(' OR ')})"

  self.class.find(:all,
                  :joins => "INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id" +
                            "INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id #{tags_join}",
                  :conditions => ["posts.id != ?", id], :group => "posts.id",
                  :order => "COUNT(*) DESC",
                  :limit => limit)
end

You can see, as I mentioned, that all the work is being done in the SQL now. I first create a list of tags from the current post, which I then feed into the query to search for other posts with similar tags. The SQL instructs the database to search for any posts with any of these tags, and then orders them based on how many tags match between the 2 posts. The SQL ended up being fairly complicated, with a lot of joins, but it’s now a whole lot faster, because I’m not creating a lot of overhead by dealing with the computation in Ruby. If you’re interested, here’s an example related entry query:

SELECT `posts`.* FROM `posts`
INNER JOIN taggings posts_taggings ON posts_taggings.taggable_id = posts.id
INNER JOIN tags posts_tags ON posts_tags.id = posts_taggings.tag_id
    AND (posts_tags.id = 695
        OR posts_tags.id = 192
        OR posts_tags.id = 195)
WHERE (posts.id != 4322) AND ( (`posts`.`type` = 'FlickrPhoto' ) )
GROUP BY posts.id
ORDER BY COUNT(*) DESC
LIMIT 5

Posted on April 13, 2009 Leave a Comment
Tagged with: , , , , , , ,

How to Take Better Photos

I’ve been into photography for a while now, and while I definitely don’t consider myself an expert on any level, I’ve come across a lot of tips over the years that have really helped to improve my photos. I’ve collected together a few of my favorite ones below, in the hopes they’ll help someone else.

Constrain yourself

Peeling Paint and Window, Venice, Italy

Peeling Paint and Window, taken with my 50mm lens

Next time you go out to take some photos, give yourself a constraint. For example, maybe just bring one lens, or choose just one focal length to shoot at (a prime lens works perfectly for this). When I went to Italy, I brought 2 lenses, one of which appeared to be broken when I got there, so I had to spend the week using only my 50mm lens. This actually turned out to be a blessing in disguise, as I ended up with some of my favorite photos I’ve ever taken. Another great exercise in constraints is to place a piece of paper on the ground, and then take 50 photos without taking your feet off the paper. Really, you’re just trying to get yourself to look at the world in a different way.

Take your camera everywhere

A lot of great photos come about because someone was in the right spot at the right time. However, in order to capture the moment, you have to have a camera to do it with. If you’re not carrying your camera with you everywhere, you’re probably missing out on a lot of opportunities. One of the best things I ever did was purchase a point and shoot camera that fit in my pocket–now I can carry it wherever I go, ready for the unexpected.

Stop worrying about equipment

Weeping Lily

Weeping Lily, taken on a tiny point and shoot!

So many times, I’ve caught myself saying, “I would love to take that photo, but my camera’s not good enough.” This is stupid. Ansel Adams didn’t have a fancy 20 megapixel DSLR, and he took photos better than most people can even dream of. Instead, work with what you have–it’s the thought behind the photo, not the camera, that truly matters. I’ve taken great photos with tiny little point and shoot cameras, and I’ve taken horrible photos with my big DSLR. The sooner you realize your equipment doesn’t matter, the better.

Ask for critique

Lauren

This photo of my sister became a whole lot better after having it critiqued on Flickr.

Some of the best photography experiences I’ve had were when I’ve had my photos critiqued by others, and when I’ve critiqued others. Having someone else take a look at your work can be incredibly helpful, as they’ll often notice things you never saw yourself, and critiquing other people’s photos can help you find new techniques and approaches to photography. If you have a group of friends that are all into photography, maybe you can meet every so often and swap shots. Or, join a site like Flickr, and start commenting on photos by other users. Soon enough, they’ll be commenting back on yours, and you’ll start seeing your photos in a whole new light.

Take lots of photos

Memory cards are cheap, so why skimp on photos? I’ve found that the more photos I take, the better I become at photography. But it’s not just about taking lots of photos, it’s about going back and seeing what your mistakes were and learning from them. If you aren’t taking a whole bunch of photos, you’re not going to learn from your mistakes, because you won’t be making them.

Don’t think about Photoshop

Dreaming

This photo, Dreaming, was weak from the start, and my attempts to save it really didn’t do much.

For a while, I was really into Photoshop–nearly every photo I took was manipulated in some way to make it better, and I started to even think about how I was going to process an image before I even pressed the shutter button. This is bad. Focus on making your photos as perfect as possible before they get to your computer, and you’ll end up with a much nicer finished product. That’s not to say you shouldn’t tweak photos afterwards, but you just have to remember that taking an amazing photo isn’t about what filters you use, but rather about how you compose the image in the viewfinder.

Hopefully these tips are helpful, and if you have any to share, make sure to leave comments below!

Posted on April 5, 2009 4 Comments
Tagged with: , ,

Recalibration

As you may or may not have noticed, I recently redesigned my site, only about six months after my last redesign. I did this for a variety of reasons, the biggest of which was how outdated and restrictive my old design was starting to feel. The new design brings more flexibility and represents a slight change in the focus of my site. It’s perhaps one of the more complete designs I’ve ever done, which is surprising, since I had it done in only about 4 days.

A Little More Personal

Old Header

Having “web design + development” in my header restricted my post topics.

One issue that always plagued me with my previous design was that I felt compelled to stick to one topic: web development. I remember showing Colin Devroe the design for the first time, and he mentioned that I should take out the “web design + development” from my header. At the time, I brushed the comment aside, thinking it was not much of an issue. As time went on, however, it became more constraining, and, now, with the new design, I’m hoping to make this into more of a personal site. Sure, I’m still going to be posting mostly about the web, since that’s one of my primary interests, but I also hope to have more posts about other things as well. I also imported all of my Flickr photos into a brand new section on the site, which I’m really excited about.

Easier to Navigate

Old Archives

My old archives were painful to browse.

With my old site, it was almost infuriating how difficult it was to discover old posts. The main archive page (pictured above) was just about useless, and it was hard to find related posts once you started browsing. With the new design, I’ve significantly cleaned up my notebook section to make it a whole lot easier to browse. I’ve even found myself browsing my old Flickr photos this way, because it’s so enjoyable. My absolute favorite part is the “filter bar” that appears on each archive page (e.g. the 2008 archives), which allows you to quickly filter out what type of post (entries, links, or photos) you want to see, no matter whether you’re browsing by tag or date. I’ve also improved the related entries algorithm, and it now is more accurate and efficient. (I posted my old algorithm a little while back.)

My New Favorite Font: Calibri

Archive Filters

The new archive filter really help to drill down what you’re looking for, and it looks great in Calibri.

When I first started mocking up the new design in Photoshop, I used Calibri, one of the new fonts in Microsoft Office, for the text, expecting to change it later. As I kept working, I eventually fell in love with the font and realized I couldn’t have the site set in anything else. However, this posed a problem, as most people do not have Calibri installed, and, because it’s sized slightly smaller than other fonts, having a fallback of Helvetica or Arial didn’t look very good. To combat that, I put together a simple jQuery plugin to detect if Calibri is installed, which allows me to serve two different versions of the site really easily. If you don’t have Calbri, however, I’d really, really recommend getting it, because it truly is a beautiful font. Check out my about page for info on where you might find it.

Separate Admin Area FTW

Back when I was working on rewriting my site, I toyed with the idea of having all of the site administration inline–that is, instead of having a separate admin area, I would be able to edit a post from the same page everyone else views it, with a sort of WYSIWYG interface. After thinking it over, I ended up keeping the two parts separate, and I’m really glad I did. When I went to put the new design into my Rails project, I didn’t have to worry about making sure my administration functions still worked, because I didn’t even touch that code–I coul d go ahead and change the design completely without breaking the core of the site. I also didn’t have to have the extra hassle of designing to accommodate an administration area as well.

Feedback

Of course, I’m constantly tweaking the site. Right now, I’ve got a list of about 15 things I still need to do, and I’m sure that will keep growing. If you have any thoughts on the new design, please let me know either by commenting on this post or by sending me an email. If you’re reading this through a feed reader, please come and check out my site and see what you’re missing!

Posted on February 21, 2009 Leave a Comment
Tagged with: , , ,

The Black Hole War

The Black Hole War I recently finished reading The Black Hole War by Leonard Susskind and really enjoyed it. Billed as “the riveting inside account of the battle between Stephen Hawking, Leonard Susskind, and Gerard ‘t Hooft over the true nature of black holes,” I ended up learning a lot from the book about black holes, quantum mechanics, and gravity.

Information Loss

The central issue at stake in the book is the idea, proposed by Stephen Hawking in 1981, that information is lost when an object is devoured by a black hole. That is to say, if you or I happened to get sucked into a black hole, when that black hole evaporated (as all eventually do), there would be no sign that we ever existed. This may seem to make sense, but it violates a central law of physics that information cannot be destroyed. Even if you think of burning a book, you may not be able to read the pages any more, but, if you were able to catch all the light given off from the fire and collect any remaining physical pieces, you could reconstruct the book as it was, so the information contained within it still exists.

Susskind, upon hearing this pronouncement, spent the next 20 years trying to disprove Hawking. Though Susskind initially was in the minority, he eventually was able to convince physicists that information is indeed conserved when an object enters a black hole. While it’s a book about some very complicated physical interactions, Susskind presents it in a way which is very accessible to the average person.

Physics as Thought Experiments and Analogies

One of my favorite aspects of Susskind’s book was how he was able to boil down even the most complex problems to simple thought experiments or analogies. For instance, to explain how gravity can bend light’s path, he used the example of a very fast elevator, and to describe what someone would look like as he or she fell into a black hole, he used an analogy of fish in a pond getting sucked down a hole in the middle. This is one of my favorite parts about studying physics–most complicated problems can be explained through simple thought experiments, which is exactly how Albert Einstein first came up with the special theory of relativity.

As Susskind got into more advanced quantum mechanical topics, I could tell he was having trouble explaining the concepts without mathematics (there were perhaps only 2 equations in the entire book). This is not his fault, as the interactions on a quantum scale are so bizarre that there really are no analogs in the real world. Humans were not made to be able to understand these things, so it requires a lot of “rewiring,” as Susskind puts it, to really understand what is going on, more so than can be done in a 450-page book. That being said, Susskind does an admirable job of giving enough insight into what is going on without having to go into the specifics.

Definitely Recommended

If you have any interest in black holes at all, I’d definitely recommend reading The Black Hole War, as it is definitely a book created so that everyone can have at least some understanding of the complexities of black holes. For me, I wish it had been a bit more on the technical side, but I can definitely understand why Susskind wrote it the way he did. If nothing else, it excited me to learn more about what are some of the strangest objects in our universe, and I’m really looking forward to go more in depth about their properties in my classes at school.

Posted on February 8, 2009 Leave a Comment
Tagged with: , , , , , ,

The Wonderful World of Web Hooks

I just read through a presentation on web hooks by Jeff Lindsay, and the concept really excited me. They allow developers to build powerful web services with little overhead and a low barrier to entry. Hopefully, over the next year or so, more applications will begin to use hooks to create a more connected and powerful web.

What are Web Hooks?

The best example of web hooks I’ve found is GitHub, a social code hosting service. In each project you create, you can specify a “post-receive hook”, which is just a URL of your choosing. Whenever someone pushes new code to the project, GitHub sends an HTTP POST to this URL, and you can do whatever you want with the data. For instance, say you wanted to send an email to your team, notifying them of the changes. You could write a simple PHP script like this:

  $data = $_POST['payload'];

  $subject = count($data['commits']) . " new commits"
  $body = "New code has been committed to the project: \n";

  foreach($data['commit'] as $commit) {
    $body .= "Commit: " . $commit['id'] . "\n";
    $body .= $commit['message'] . "\n\n";
  }

  mail($recipients, $subject, $body);

Now, if you set the URL of this script as your post-receive hook, whenever someone pushes code, GitHub POSTs the data to the URL, and an email is sent to your team. Not only is this really powerful, but it’s also much more efficient than traditional methods.

Web Hooks = Efficiency

Suppose you wanted to build an app with the same functionality as described above, but without using hooks. You would need to create a script that grabbed your project’s RSS feed, parsed it, and sent an email if there’s new information. This wouldn’t work nearly as well as before. Look at the script above: 8 lines of code. With the added steps of fetching the feed and parsing it, you’re adding considerable complexity to your code, plus you need a way to tell what content is new, so you don’t send out duplicate emails. Not only is this a lot of steps, but you also need to schedule it to run on a consistent basis using a cron job or something similar.

This all means you’re putting a greater load on both your application and on GitHub. Much of the time, when your script is run, it won’t find any new data, wasting both GitHub’s and your resources. With a web hook, your script waits patiently until something happens, and is only run when there’s a reason. It’s also run immediately after the action occurs, i.e. when GitHub receives a commit, it pings your script right away. If you have to poll an RSS feed, you might not know for a while, depending on how often the script is run.

The Future

Where hooks will really succeed, I think, are as lightweight web services, like Jeff’s MailHook, a service that receives emails and POSTs the information to your hook. Jeff took it down, since it was more of a proof-of-concept than anything else, but applications like this are really exciting. I’d definitely recommend looking through Jeff’s presentation (if you haven’t already), as he has some great examples of existing sites that use web hooks, as well as insight into the future of this method of interaction.

The aspect that excites me the most is how easy it is to write a web hook. All you need to have is a cheap PHP host and some basic knowledge about handling POST data, and you’re ready to go. By shortening the learning curve, web hooks make it possible for more people to build on top of existing platforms, leading to a more connected and interesting internet.

Posted on January 8, 2009 Leave a Comment
Tagged with: , , ,

Making Better Use of named_scope

In Ruby on Rails 2.1, a great little feature called named_scope was added that really makes complicated finds a whole lot easier. I’m going to walk through one way you can use scopes to clean up your code, and if you want more information, Ryan Daigle’s post is a good starting point.

The Situation

For a project I’m working on, users can submit reviews, and I needed a way to access a user’s friends’ reviews. At first, I considered adding a method like the following to my User model:

class User < ActiveRecord::Base
  def friends_reviews
    Review.find(:all,
      :joins      => "JOIN friendships ON user_id = #{self.id}",
      :conditions => "friendships.friend_id = reviews.user_id"
    )
  end
end

Named Scopes to the Rescue

While this method would work fine, there are several issues. For one, what if I want to change parameters on the find, like limiting it to 5 entries, or sorting by date? I would have to add parameters to the method, and it would start to get complicated. Instead, I created a scope in my Review model:

class Review < ActiveRecord::Base
  named_scope :by_friends_of, lambda { |user|
    {
      :joins      => "JOIN friendships ON user_id = #{user.id}",
      :conditions => "friendships.friend_id = reviews.user_id"
    }
  }
end

Now, to get the reviews I want, I can call Review.by_friends_of(user) and it will get me reviews by user’s friends. What’s even better, since it’s a scope, I can modify it. For instance, Review.by_friends_of(user).all(:limit => 10, :order => "created_at DESC") will limit it to 10 reviews, sorted by creation date. I can even use other scopes I might have created, like: Review.by_friends_of(user).published.all(:limit => 20), and so on.

Scopes are super useful, and recently I’ve started to use them for just about everything. Definitely try them out, they’ll make your code a lot more efficient and useful.

Posted on January 6, 2009 1 Comment
Tagged with: , , , , , ,