Do you like see-food? See? Food.

Brownie Sundae

The nice thing about some restaurants is they pay extra special attention to presentation. That way if the food isn’t to your taste, maybe it is at least pleasing to your eyes. We know that just a list of each dish and comments about said dish served at a restaurant isn’t going to fly. But if we spruced it up with member-submitted pictures… well… now our eyes have something to see other than text and rating stars.

Now, who wants a sundae in a boat!

Restaurant Reviews By Us.

Read full post
Restaurants can be (tagged) spicy too!

We had a change of heart on tagging. Tag everything! Initially I felt that only menu items should be tagged. “People want to know what dishes are spicy“… true, but people also want to know what restaurants are in Uptown or are Mexican or are closed on mondays. So… we now have restaurant tags. Login and tag something!

Read full post
How to remove file extensions from URLs

URLs should be treated as prime real estate. Their primary purpose is to locate resources on the Internet. So for a web developer, it makes sense to make things as user-friendly as possible. One effort is to remove the extensions from files. I don’t mean things like .html or .pdf, as those give you an idea that you’re reading a page of content or a PDF document. I meant things like .php or .asp or .pl, etc. These are unnecessary items that just clutter the location bar on most browsers.

There are two ways to do this. The easy way which just looks at a request, if the requested filename doesn’t exist, then it looks for the filename with a .php (or .asp or whatever) extension. In an .htaccess file:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ $1.php [L,QSA]

Now if you go to: http://domain/about the server will interpret it as if you went to http://domain/about.php.

Makes sense, but if we’re already breaking the relation between URL and filename, we may as well break it intelligently. Change that .htaccess file:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

Now if you go to: http://domain/about the server will interpret it as if you went to http://domain/index.php?q=about. How is this useful? Well now index.php is always called, so it can do anything common to all pages (which might be nothing) and do something based on the $_GET['q'] variable.

For example:

require_once('html_functions.php');
switch ($_GET['q']) {
	case 'about':
		echo myhead('about page');
		break;
	default:
		echo myhead('home page')
		break;
}
include($_GET['q'] . '.php');
echo myfoot();

We’re loading a hypothetical library html_functions.php which contains some simple functions (myhead() and myfoot()) that print out a simple header or footer for this site. The switch statement dynamically sets the <head/>. After the switch we include a file based on the query string. In our case it will still pull up about.php. Granted, this is not what I use personally, but it’s the general idea behind how symfony works.

Why?

So why go through all this nonsense? Extensions for the most part don’t mean much to an end user. Sure, jpg, png or gif mean images and html mean web page and pdf means the file is a PDF document. Dynamic pages, however usually come from cgi, php, pl , asp pages or some other 3 or 4 letter extension that the server uses as a hint to determine how to parse, but the output is usually html. Servers are smart though. They don’t need hints, and the above code eliminates the need to reveal so explicitly just how a page is delivered. Take our restaurant review site, for the most part you can’t tell that it’s done in php. In fact all the URLs are “clean” and somewhat logical. The benefit of having clean simple URLs is if we decide to change from PHP to ASP for example, we won’t need to change our URLs.

Read full post
Menu item versions

We make a lot of mistakes, and by we, I don’t mean myself, but I mean we as humanity. That’s why we like to keep multiple versions of things in our restaurant review database. That also means menu items. Did you describe the Pesto Chicken as having pisto and not pesto? Problem solved, now any user can log in and change it. It’s like a wiki…. for restaurant menus.

Read full post
Inverting color codes in Textmate

I deal a lot with hex color codes in CSS. One thing I occasionally need to do is invert color codes. Normally this is something I could Google for, but I wanted a solution that didn’t requiring constant reference.

My favorite text editor, Textmate, has a powerful automation system. I can write mini scripts in whatever language suitable and take advantage of the power of Unix shell scripting to execute them. From Googling, I learned enough ruby to learn that this:

printf("#%06X", 0xFFFFFF - STDIN.gets.gsub(/^#/,"").hex )

Will invert a hex color from standard input. What it’s doing is fairly simple. It’s using printf to print a formatted string. %06X means it should zero-fill the resulting string with up to six zeros, the same way a hex color string is (e.g. we write 0000FF and not FF to mean ‘blue’). The rest is simple subtraction. We take FFFFFF, the hex code for white, and subtract the input from STDIN and arrive at the inverse of what we started. Now to add this to TextMate we open Automation|Run Command|Edit Commands... and create a new command:

echo $TM_SELECTED_TEXT |ruby -e 'printf("#%06X", 0xFFFFFF - STDIN.gets.gsub(/^#/,"").hex )' 

This echo’s whatever is selected and pipes it to the ruby script. We set the command to input selected text and replace the selected text on output. Furthermore we can bind it to a keystroke. I chose Control-Alt-I, as it is unused on my system.

Voila, I can highlight any hex code and instantly invert it.

To keep this on one line, I neglected a few friendly features. One is interpreting 3-digit hex colors (e.g. #ccc), and the other is knowing whether or not to place the # in the result. If you can come up with an elegant solution, please post it below. Otherwise I hope this helps.

Read full post
Syncing with symfony and clearing your cache in one shot

If you’re using symfony’s sync command to synchronize files across environments (e.g. moving your development files to a staging server), it helps usually to clear the cache of the receiving server.

The following line will help:

symfony sync production go ; ssh user@production "cd /var/www; symfony cc"   

Assuming you have SSH keys defined and that you change user@production to your username and server host as well as /var/www switched to your website path. Also the symfony command needs to work on your “production” server (or whatever environment).

There may be a cleaner way to take care of this by changing the symfony command, but this works sufficiently well. One of the more frustrating elements of web development is synchronizing multiple sites: usually a development site, a staging site and a production site. SVN helps with keeping your code versioned, but usually you don’t want to check out a copy of your web site onto your live server.

Usually we use SFTP or rsync. The former has lots of problems, because a lot of manual work is usually involved to make sure you don’t over-write important files.

rsync, however, is a champ and symfony takes advantage of this. The key files you’ll have to deal with are $PROJECT_HOME/config/properties.ini and $PROJECT_HOME/config/rsync_exclude.txt.

Your properties.ini should look roughly like:

[symfony]
  name=reviewsby.us
[staging]
  host=staging.reviewsby.us
  port=22
  user=root
  dir=/var/www/staging/
[staging2]
  host=staging2.reviewsby.us
  port=22
  user=root
  dir=/var/www/staging

Each heading other than “[symfony]” is a different environment. In our example, we have two staging environments. The values under each heading should be self-explanatory. We can now run the following commands:

symfony sync staging
symfony sync staging go
symfony sync staging2
symfony sync staging2 go

The commands that lack go are “dry-runs” which just show you what files will be transfered. The other commands will run rsync and transfer all the files not specified in the exclude file, rsync_exclude.txt.

See askeet or the symfony documentation for more details.

Read full post
My bike was stolen

Details are on Metroblogging

Read full post
Safari Fixes

Safari interprets /* */s differently than FireFox or IE. FF and IE will ignore a unmatched /* or */, whereas Safari will ignore parts of code if there’s a lone */. Once I found that out, I was able to get the list items that are used throughout the site to render properly.

Read full post
Going international... kinda

Some of the first non-Minnesotan restaurants to show up were Flying Dog, Bangalore and Konstam… all of them outside the US. Wasn’t expecting that… but then again, I wasn’t really surprised.

I finally updated our location tables to account for different countries. Currently it’ll only plot what it can Geocode, and relies exclusively on Yahoo! for GeoCoding.

I’ve only tested this with a Canadian restaurant. Hopefully it’ll work elsewhere soon. If anybody plans on adding any non-US, non-Canadian restaurants, let me know if you can figure out how to GeoCode things properly.

Also, I’m pleased as punch that the map on the homepage shows three states as having recent restaurants. Rock on!

Read full post