Skip to main content

Posts

Showing posts from August, 2009

Rails: Ratings

I had to add a ratings widget to my app, sort of like Amazon has. I decided to use the jQuery Star Rating Plugin on the front end. That worked out well. I decided to code the back end from scratch. That took longer than I would have expected, but the code is super tight.

Only logged in users can vote. If a user votes again, it should update his existing vote rather than letting him stuff the ballot box. Aside from keeping track of the ratings for each user, I wanted the item itself, i.e. the book, to have a rating_average field. Furthermore, I didn't want rating_average to have to calculate the average rating every time I loaded the page. It should be cached in the same way that Rails can cache the number of children a parent has.

Here's what my schema looks like:class CreateBookRatings < ActiveRecord::Migration
def self.up
execute %{
CREATE TABLE book_ratings (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
book_id INT…

JavaScript: Ajax Notifications with jQuery

If you're using Ajax, you should give hints to the user when a request to the server is being made, and you should tell him if something goes wrong. Gmail does this really well. Here's how I coded it:

First, add this to your layout:<div id="ajaxNotifications"></div>Add some CSS to make it purty:#ajaxNotifications {
position: fixed;
top: 0px;
right: 0px;
background: yellow;
text-color: black;
padding: 5px;
display: none;
}Setup jQuery, jQuery UI, and your own application.js file. This syntax is for Rails, but it's similar in Python:<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
<% extras = RAILS_ENV == "development" ? "{ uncompressed: true }" : "{}" %>
google.load("jquery", "1.3.2", <%= extras %>);
google.load("jqueryui", "1.7.2", <%= extras %>);
</script>…

Rails: Engine Yard Flex

I'm reading the Engine Yard Flex documentation. It's pretty interesting. Here's a snippet:Each Application or Application Master server is setup to run haproxy on port 80 and then nginx or apache on port 81. Each App server has its own haproxy setup to balance load to all the other App servers so any one App server could become master at any point if the main master fails for any reason. We have an 'ey-monitor' daemon that runs on all the application slave servers and periodically does health checks on the current Application Master server to see if it is still running properly or not. If the App Master fails for any reason then the App slaves will try to take over as master by using a technique called STONITH(shoot the other node in the head). This means that once the master fails, the slaves will wait for a few bad health checks and then the slaves will all race to grab a distributed lock. Whichever slave gets the lock will steal the IP address of the failing m…

JavaScript: Coping with Client Server Conflicts

One of the things that worries me about writing Ajax apps is how easy it is for the client and the server to get out of sync. Open up the app in two different browsers, and it's easy to play havoc. Even if you implement Comet, all you have to do is temporarily lose Internet connectivity to go out of sync.

One way to cope with this problem is by using the "409 Conflict" HTTP status code. If the client makes a request, and the server knows that the client is working with stale state, it can return a "409 Conflict". In my own case, if the client makes an Ajax request, and the server returns a "409 Conflict", the client will reload the current page. It's not perfect, but it's a simple, effect, and easily-implemented strategy.

Here's how to return a "409 Conflict" in Rails:
render :text => "Your browser is out of sync with the server. Please reload the page.",
:status => "409 Conflict"Here's ho…

Rails: Using jQuery via Google's AJAX Libraries API

Adding jQuery to your project via Google's AJAX Libraries API is a piece of cake! I just updated my application layout to include:<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
<% extras = RAILS_ENV == "development" ? "{ uncompressed: true }": "{}" %>
google.load("jquery", "1.3.2", <%= extras %>);
google.load("jqueryui", "1.7.2", <%= extras %>);
</script>That code loads jquery and jqueryui. I don't have to keep them on my server. I don't have to cache them. It uses the compressed version for production and the uncompressed version in development. Best of all, the user very well might already have that file cached in their browser.

JavaScript: DOM vs. innerHTML, Server-driven vs. Client-driven

What's the best approach to architecting JavaScript, and which frameworks best support that approach? Is it best to build the app mostly on the client like Gmail and Google Maps, or is it better to provide a normal HTML page, but with lots of Ajax mixed in like YouTube? Which approach leads to the fewest bugs when the client and server get out of sync? How does your server respond to Ajax requests? Does it serve up JavaScript code to run, JSON or XML data to digest, or pre-rendered HTML?

In the Rails world, there are all these helper functions that generate JavaScript in your HTML pages. The JavaScript might result in Ajax requests that themselves serve up more JavaScript (via .js.erb or .rjs files). There is also heavy use of innerHTML. The server is in control of the application flow.

In the jQuery world, it's standard to keep the JavaScript separate of the HTML. I think innerHTML use is still very common via the append() method.

It seems like having the client be in co…

Web: More Lost than Ever

I've built Web apps in ASP, PHP, Perl (with mod_perl and Mason), Python, and Ruby. Just in the Python world, I've used Webware, Aquarium (which I wrote), Zope, Plone, Web.py, Pylons, and Django. I've also used ZPT, Cheetah, Myghty, Mako, Genshi, and Django templates. On the JavaScript side, I've used MochiKit, Dojo, and jQuery, and I've read the docs for YUI, Prototype, and Script.aculo.us. I've written Greasemonkey scripts, and read all the XUL documentation. I've used Apache, Nginx, IIS, and I've even written my own Web server. I've done ecommerce sites, configuration dashboards, XML-RPC services, RESTful Web services, and Ajax apps. I do everything from CSS to scalability. I'm even up to date with the latest books: I've read "JavaScript the Good Parts", "Building Scalable Web Sites", "RESTful Web Services", etc.

After a decade of working with the Web, you might think I'd know it like the back of m…

Rails: Dynamic 404s, authlogic, Cucumber, and rescue_from

I'm using Rails, Cucumber, and authlogic. I want my 404 pages to be rendered dynamically; that way, I can use my application-wide layout, which is a lot friendlier for lost users. I want ActiveRecord::RecordNotFound exceptions to be handled by the dynamic 404 page. I want Cucumber tests to verify that everything is working correctly.

Getting everything working together at the same time turned out to be extremely challenging. Cucumber made it hard for me to test my ActiveRecord::RecordNotFound handling. authlogic made it impossible for me to catch ActionController::RoutingError exceptions. Defining ApplicationController#render_optional_error_file as described here conflicted with authlogic and/or Cucumber. After several hours, I finally got it all working. Here's how:

First, I added the following to ApplicationController:# Note, I can't use rescue_from to catch ActionController::RoutingError,
# otherwise authlogic breaks. Hence, I set a default route instead.
rescue_fr…

Rails: authlogic Code Review

I just spent a couple days code reviewing authlogic. First of all, let me say it's good code. Usually, I have a lot to say when I code review someone's code, but this time, I was pretty happy. Here are the corrections I submitted.

Clearly, the author of authlogic knows Ruby a heck of a lot better than I do; I'm still relatively new to Ruby. There were a bunch of idioms, language features, and design decisions that caught my attention as a Python programmer. That's what this blog post is about.

The first thing I noticed is that the code pieces together huge classes by mixing in tons of modules. For instance, in authlogic/session/base.rb:module Authlogic
module Session
# This is the base class Authlogic, where all modules are included. For
# information on functionality see the various sub modules.
class Base
include Foundation
include Callbacks
include Timeout
include Params
include Cookies
include Session
include HttpAut…

Vim: Transparency in MacVim

Wahoo! MacVim now supports transparency. Finally, I can have a nice, transparent Vim window to match my transparent terminal, and I don't have to give up all the modern conveniences. If you use the "Homebrew" profile for your Terminal.app, try putting the following in your .gvimrc:colorscheme torte
set transparency=15

Open Source: Closed Source Video Games

About a year ago, my buddy Ben Bangert gave us his old GameCube. My wife and I really enjoyed playing Paper Mario: The Thousand-Year Door.

"Paper Mario" is a closed source game. I bought it used at my local GameStop. Since I'm a bit of a free software nut, you might wonder how I could live with the thought of playing a closed source game. The fact of the matter is, I don't think "Paper Mario" could be produced in an open source manner. Technically, I'm sure it could, but who would want to? A hundred people or more were involved in producing that game, and what do they get out of it? I wouldn't volunteer on such a project--I have real work to do! If I can't imagine volunteering my time on such a project, then why would I expect anyone else to? Instead, I paid money. I'm happy because I have a fun game to play. The developers are happy because they got paid. Believe it or not, it's a win win situation.

However, I do have enough t…

Linux: OpenGEU

Continuing from my post yesterday, Linux: Open Source and my MacBook, I gave OpenGEU 8.10 a shot under VMware Fusion on my MacBook.

OpenGEU is an Ubuntu-based Linux distribution that uses E17 (aka the unstable version of Enlightenment). The project was started and designed by the Italian artist Luca D.M. (aka TheDarkMaster).

In short, it isn't quite as "cohesive" from a feature point of view as stock Ubuntu, but it's stunningly beautiful and performs a lot better. I gave VMware 780MB of RAM, but it's currently using only half that. It also seems to use a lot less CPU. It's actually tolerable under VMware.

Here are some problems I encountered and the solutions I came up with:

Don't use "Linux Easy Install". VMware claims that it can install the ISO automatically since it's based on Ubuntu. However, this just lead to a blank screen for me.

I told the installer to log me in automatically. Why not? I already have to authenticate with my Mac. …

Rails: Cache Inconsistency Caused by a Common Rails Idiom

Rails has a feature called "flash". Anything put in the flash is available on the next page load. It's based on the session, but it goes away after the next page load. It's perfect for saving a message for the user even if you're going to do a redirect.

Rails also has a function called reset_session that wipes the user's session and gives him a new one. Agile Web Development with Rails says you should call the reset_session method after the user logs out of your site. This helps avoid session fixation attacks.

Unfortunately, authlogic doesn't do this automatically. Hence, I decided to do it myself. I had code like:reset_session
flash[:notice] = "Log out successful!"The code works, but the message "Log out successful!" doesn't show up. Fortunately, my tests caught that. It turns out that Rails has a known bug that if you call reset_session, flash breaks. Why?

Rails uses an idiom that looks like:def foo
@foo ||= calculate_foo
e…

Linux: Open Source and my MacBook

I have a love hate relationship with my MacBook. I adore the little bugger. I love how everything just works. I like the screen. I like the software. However, I really don't have much love for Apple, the company. I've been getting the itch to edge a little bit more toward the open source side. This should not be surprising since I'm a Linux zealot at heart.

One thing that was a real turnoff for me is that Apple stopped releasing binary installers for standalone Darwin. The PureDarwin project is hoping to pickup where OpenDarwin left off, however, it's obviously tough going. I'll keep my eye on it, but I don't think it'll be production-ready any time soon.

I've given up, to some degree, on VMware and VirtualBox. They're useful, but they're not the sort of thing you want to live in on a day-to-day basis. They're just not fast enough. They're better for occasional use.

I was really intrigued to discover Elive. Some guy took Debian…

Rails: Foreign Key Constraints

I like referential integrity. Rails doesn't provide support for database-level foreign key constraints. "Agile Web Development with Rails" uses code such as the following to add foreign key constraints:t.integer :product_id, :null => false,
:options => "CONSTRAINT fk_line_item_products REFERENCES products(id)"That doesn't work for me. The migration ran just fine, but when I looked at the database, the constraint wasn't present. I hate it when things fail silently!

I tried a plugin to add foreign key constraints, but I got frustrated pretty quickly. I fell back to executing plain SQL:execute %{
CREATE TABLE roles_users (
role_id INT NOT NULL,
user_id INT NOT NULL,

FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX (role_id),
INDEX (user_id)
) ENGINE = INNODB
}The nice thing about plain SQL is that I can understand exactly what's going on. Th…