December 2005 Archives

% ruby -v -ryaml -e 'y({:a => 2})'
ruby 1.8.4 (2005-12-22) [powerpc-darwin8.3.0]
--- 
:a: 2

Confirmed kill!

This should fix all 1.8-related rubygem compatibility issues that occurred in 1.8.3.

304 Not Modified for Rails

| | Comments (0)

The HTTP specification allows browsers to perform conditional requests avoiding use of extra bandwidth if the page was not modified. In 43 Things I added support for conditional HTTP requests to some of our RSS feeds to save bandwidth.

As an alternative you can use the built-in page caching which uses the web server to automatically handle conditional requests, but this is not always appropriate. If you have content that is customized for a user, like home page links for logged-in users, the page cache won't work because different users will have different home page links. In our case it was simpler to add a few lines to our code than to set up a shared space to use as a page cache.

The solution I chose can be applied to any page you can determine a last modified time for.

Inline::ObjC

| | Comments (0)
Nuno Cruces provided me the following gem (hacked up by yours truly):
require 'inline'

class Inline::ObjC < Inline::C
  def initialize(mod)
    super(mod)
  end

  def import(header)
    @src << "#import #{header}"
  end
end

class MyClass
  inline(:ObjC) do |builder|
    builder.import "<Foundation/NSString.h>"
    builder.add_compile_flags '-x objective-c', '-framework Foundation'

    builder.c %q{
      void test() {
        printf("%s\n", [@"Hello World!" cString]);
      }
    }
  end
end

MyClass.new.test
I love it... OSX hackers, have at it...

Splat is good for you

| | Comments (2)
A recent post on projectionist shows two code examples:
  def foo(*args)
    [args].flatten.map do |arg|
      # ...
    end
  end

  def foo(*args)
    Array(args).map do |arg|
      # ...
    end
  end
and argues their (ugly) necessity. I think both examples fail to hit the mark (or there is a typo?) as they've ensured the initial codition simply by using *args. Allowing them to state the problem in a straightforward fashion:
  def foo(*args)
    args.flatten.map do |arg| # if you are trying to combine a bunch of arrays
      # ...
    end
  end

  def foo(*args)
    args.map do |arg|
      # ...
    end
  end
which, is much more natural. If I had to guess, I'd say they meant to not put the splat in front of args. I find using splat to be a better fit.

A while back I was thinking about MVC and how URLs are built and came to the conclusion that every model should know what its URL is. I don't think it is the responsibility of the Controller to know this, and certainly not the view.

Since Rails uses a Hash to represent a URL, I thought it would be easy to just add a method to every Model that would return a Hash representation of the URL and override url_for to perform the expansion from a model into its URL Hash.

It turns out that it was easy. After some enhancements by Ryan that adds defaults we came up with the following:

class ApplicationController < ActionController::Base
  def url_for(options = {}, *params)
    if options.include? :model then
      model = options.delete(:model)
      defaults = {
        :action => 'show',
        :id => model.id,
        :controller => model.class.name.downcase,
      }
      defaults = defaults.merge model.url_params if model.respond_to? :url_params 
      options.delete :only_path if defaults.include? :only_path
      options = defaults.merge options
    end
        
    return super(options, *params)
  end
end

You can use the modified url_for like this:

@model = Model.find some_id
url_for :model => @model
url_for :model => @model, :action => 'edit'
redirect_to :model => @model
link_to @model.name, :model => @model

Sometimes the defaults aren't good enough though. To override them you add a url_params method to your model. In Trackmap I store enough information about a flickr photo to generate its flickr URL in the database and my url_for modification (with a bit of cheating) makes this very clean:

class Photo < ActiveRecord::Base

  #...

  def url_params
    return { :host => 'flickr.com', :controller => 'photos',
             :action => flickr_nsid, :id => flickr_photo_id,
             :only_path => false }
  end

end