Monday, March 30, 2009

Managing the draw of a single-elimination tournament

My company has been running a GO tournament and since its around NCAA March Madness time I thought it'd be easy to find a site to manage the draw. I looked but couldn't find anything that did what I wanted so I wrote my own. Caveat: I just spent a few days on this and it could stand to be enhanced with more features and a rewrite of the ui implementation but I'm open to suggestions if anyone finds this useful.

To use it

  1. Edit the file lib/names.txt with a the players in your tournament

  2. Run 'rake create_tournament



Now when you view the site you'll see a draw that looks something like this.



For games with 2 players - all first round games and games where the previous round winners have been determined - you'll be able to click the link and set which player won.

This was all very quickly thrown together but if you're interested in hosting any sort of single-elimination tournament check it out on github tournament-draw.

Let me know if you use it and how you'd like to enhance it from the quick application it is today.

Sunday, March 22, 2009

Link: Search Engine in 200 lines of Ruby Code

I read a very interesting article by someone who works for Yahoo about how he wrote a basic search engine in Ruby in just a few files. I wouldn't use this for a production system for as an example of how search engines work its fascinating and I plan to keep following his site to see how he enhances it

Description: http://blog.saush.com/2009/03/write-an-internet-search-engine-with-200-lines-of-ruby-code/

Source: http://github.com/sausheong/saushengine

... and he's got a really cool css/javascript formatter for the source in his blog. I've gotta learn more about SyntaxHighlighter

Wednesday, March 18, 2009

Java as a Legacy Language

I came across this article titled Java as Legacy Language today. As an ex-Java guy who is now committed to Ruby I was amused by the title but also think he makes a good point.


One thing is for sure: If you're in the software development business, don't cling to old ways of doing development. And also, don't get too carried away thinking that something like Scrum is going to be the Bandaid that fixes your agility problems, because it may turn out that your main problem is Java itself. Keep an open mind. Try new things. Be ready when the next disruption arrives, or you may find yourself without a chair when the music stops.


I don't think Java is "bad" but I do think it encourages you to write big, complicated applications to solve big, complicated problems. In contrast I find Rails encourages you to realize most of the apps we write are in fact fairly simple and do the same CRUD steps over and over. Having so much support for that built into the framework helps you think about what's "interesting" about your appplication which may be unique but is probably not big.

Add to this how easy it is to test in Rails - tools like RSpec & Shoulda, mocking tools, integration testing and more recently cucumber + webrat all make it easier to practice TDD than not to. While there are tools in Java, the fact that they're harder to use, I believe, means fewer developers will use them and these practices will remain less ingrained in that community.

Using scopes in auto_complete plugin

My colleague Pat Shaughnessy has spent a lot of time recently enhancing the auto_complete plugin. I suggest you read his blog posts and check out his fork of auto_complete on github to see the details.

I was reading his latest change to allow filtering of auto complete picklists and really like what he did but thought there was one thing that didn't quite feel right - the fact that you have to mix the application logic to filter the list with the plugin logic to find the list in the block in your controller.

Here's the code Pat wrote in his controller and what I'd like to avoid is having to re-implement the "LOWER(tasks.name) LIKE ?" portion that's already implemented in the filtered_auto_complete_for method of autocomplete.rb in the plugin.


# For task name auto complete, only display tasks
# that belong to the given project:
filtered_auto_complete_for :task, :name do | find_options, params|
find_options.merge!(
{
:include => :project,
:conditions => [ "LOWER(tasks.name) LIKE ? AND projects.name = ?",
'%' + params['task']['name'].downcase + '%',
params['project'] ],
:order => "tasks.name ASC"
}
)
end



I'd like to propose a slight modification so the block you write as part of your application can focus just on the filtering and leave the responsibility for the search with plugin. I'm also proposing we use scopes (which I don't think were around when the original auto_complete plugin was written) to filter and sort the list the plugin. Here is the code I'd like to write in my application.


class ProjectController < ApplicationController
# For task name auto complete, only display tasks
# that belong to the given project:
filtered_auto_complete_for :task, :name do | list, params |
list.by_project(params['project']['id'])
end
end

class Project < ActiveRecord::Base
named_scope :by_project,
lambda {|project_id| {:conditions => {:project_id => project_id} } }
end


We can do this with a simple modification of the plugin implementation of filtered_auto_complete_for


def filtered_auto_complete_for(object, method)
define_method("auto_complete_for_#{object}_#{method}") do
find_options = {
:conditions => [ "LOWER(#{method}) LIKE ?", '%' +
params[object][method].downcase + '%' ],
:order => "#{method} ASC",
:limit => 10 }
@items = object.to_s.camelize.constantize.scoped(find_options)
@items = yield(@items, params) if block_given?

render :inline => "<%= auto_complete_result @items, '#{method}' %>"
end
end


Originally, it used passed the find_options hash to the block and then executed the search in one step as "@items = object.to_s.camelize.constantize.find(:all, find_options)". My change is to rely on ActiveRecord proxy objects to chain criteria together leaving the block independent of the criteria here in the plugin. The best part is you don't have to worry about performance as ActiveRecord will intelligently combine the criteria into a single SQL request. For example searching for tasks that start with 'tas' within project #7


User Load (1.2ms) SELECT * FROM `tasks` WHERE ((`tasks`.`project_id` = '7') AND (LOWER(name) LIKE '%tas%')) ORDER BY name ASC LIMIT 10

Wednesday, March 4, 2009

Microsoft Office links causing InvalidAuthenticityToken in Rails

I started receiving a lot of error notifications recently from my ExceptionNotfier plugin for an error with ActionController::InvalidAuthenticityToken. It turned out the error was occurring because one of my users was pasting a link to my app in an MS Office document and when Office sees the link it makes a request that Rails could not handle. Here I'll show you a simple fix you can use to avoid these errors with much credit going to an article at
Dealing with Microsoft Office Protocol Discovery in Rails
.

My execptions looked something like this (lots of boring details omitted)


A ActionController::InvalidAuthenticityToken occurred in events#1164:

ActionController::InvalidAuthenticityToken
[RAILS_ROOT]/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'

-------------------------------
Environment:
-------------------------------
* HTTP_USER_AGENT : Microsoft Data Access Internet Publishing Provider Protocol Discovery
* REQUEST_METHOD : OPTIONS


The problem is that Rails doesn't understand the method 'OPTIONS' (see rails/actionpack/lib/action_controller/routing.rb


#around line 270 of routing.rb
module ActionController
module Routing
HTTP_METHODS = [:get, :head, :post, :put, :delete]
end
end


Fixing the problem is fairly simple. You insert a before_filter into your application controller to intercept and handle requests with the option method before the rails code realizes it can't handle the request.


class ApplicationController < ActionController::Base
before_filter :options_for_microsoft_office_protocol_discovery

...

def options_for_microsoft_office_protocol_discovery
render :nothing => true, :status => 200 if request.method == :options
end
end


Its also easy to write a simple spec in rspec to verify the behavior. There is one trick which is that rails/actionpack/lib/action_controller/test_process.rb defines helper methods for get, post, put, delete & head that we can't use so we need to call the underlying method directly but the signature for that underlying method changed with Rails 2.3 (commit) so depending what version you're using you'll need one of 2 flavors.


# Rails 2.3 and above
it 'should not throw an exception on OPTIONS request (from ms office protocol discovery)' do
process '/any/goofy/path', nil, nil, nil, 'OPTIONS'
response.should be_success
end

# Rails < 2.3 version
it 'should not throw an exception on OPTIONS request (from ms office protocol discovery)' do
@request.env['REQUEST_METHOD'] = 'OPTIONS'
process '/any/goofy/path'
response.should be_success
end


You can also test from the command line using curl


curl -X OPTIONS http://localhost:3000/


Update: It turns out that Rails stores the acceptable methods in 2 different places actionpack/lib/action_controller/request.rb which does include all of get head put post delete options and also (see rails/actionpack/lib/action_controller/routing.rb which only includes :get, :head, :post, :put, :delete (options is missing). This means this fix will only work for OPTIONS requests and not any other type as ActionController::Request request_method will throw an exception before getting to the filter code above.