RubyGreenBlue

iPhone Released

Posted by keith 55 days ago
Vote for this article on digg.com: Digg It!

Finally Apple have announced the much rumoured iPhone. What a cool device. They have redefined cell phones, and in my opinion hand-held devices in general. I don't remember any new gadget announcement bringing me to say the word "fuck" as many times as the introduction of the iPhone did.

A while back I wrote about multi-functional devices and polymorphic UIs. I had some pretty lofty ideas and hopes for an iPod / iPhone multi-functional gadget and I have to say I was blown away with the iPhone.

I think Apple has nailed it.

For me one of the neatest things about the device is that it runs OS X - or maybe some kind of cut down version. Presumably I will be able to ssh into my iPhone and do some ruby scripting. I'm sure someone will write an app that can be used to control other devices in the home - salling clicker style.

Most popular story on digg by far.

The news of the iPhone has turned out to be wildly popular. AAPL shares rose by some 8% and iPhone was pretty much the only news in Steve's keynote.

On digg.com this story is the most popular story in the last 365 days and it's barely 13 hours old.

The stock market was also impressed

Take a look at the graph of AAPL against PALM and RIMM (makers of blackberry) as at close of trading on the 9th January 2007 (thanks to daringfireball for that link)

picture taken from finance.yahoo.com


Finding Descendants of a Class

Posted by keith 82 days ago

Ruby offers a method for finding all the ancestors of a class (ancestors funnily enough) but, as far as I know, no easy way to get at all the descendants of a class.

I thought it had to be possible, this *is* Ruby after all! But in thinking about it a little more, I figured it could well be a tricky thing to do and probably require some hook in the Ruby language for it all to be even possible.

Luckily I found this beauty: ObjectSpace. With ObjectSpace you can eacherate over every object in the Ruby object space. What else?

So it tuns out that finding all the descendants of a particular class is not such a big deal after all. Just eacherate over ObjectSpace and do a little recursion:

class Object

  def self.show_descendants(klass = nil, indent_level = 1)
    unless klass
      klass = self
      puts ' - ' * (indent_level) + klass.to_s
    end
    ObjectSpace.each_object do |obj|
      if obj.class == Class and obj.superclass == klass
        puts ' - ' * (indent_level + 1) + obj.to_s
        show_descendants(obj, indent_level + 1)
      end
    end
  end

  def show_descendants(klass, indent_level = 1)
    klass.show_descendants(nil, indent_level)
  end

end


# you can pass the class as an argument
show_descendants(ScriptError)

# or you can send the method to the class
Numeric.show_descendants

This code could be modified a little to return a list of descendants, each containing their own list of descendants rather than putsing it all to the screen.


Remote Editing with TextMate

Posted by keith 82 days ago

TextMate does not have a save over sftp function, and I'm not sure Allan has any plans to add it in. Sometimes it's useful to edit files remotely. I often run my development server on another (faster) machine and do the actual editing on my trusty old (now slightly slow) PowerBook.

Solution

The solution is fairly rudimentary and simple but it works:

  • create a TextMate project. Create a real TextMate project rather than temporary one as we will want to save some project variables.

  • setup your project variables.

    setup two project variables

    • $TM_REMOTE_SERVER. Set this to something like keith@servername:
    • $TM_REMOTE_SERVER_PORT. Set this to 22 unless you have your ssh access setup over some other port
  • create a snippet or better still, create a new bundle called something like "Remote Editing" and then a new snippet in that bundle. The snippet code:

    if [ "$TMREMOTESERVER" != "" ] then scp -P{$TMREMOTESERVERPORT#22} -oStrictHostKeyChecking=no $TMFILEPATH $TMREMOTESERVER$TM_FILEPATH fi

    Have the snippet save the current file, output as a tooltip and set its key equivalent to APPLE-s. It should all look something like this:

One limitation of this system is that the directory structure on both local and remote machines needs to be the same. It would be possible to address this issue, but mirroring my directory structures for projects I want edit remotely has not been a big deal for me. Basically you setup a /User/UserName/projects dir on each machine and everything is ok.

You also need to have ssh public key authentication and an ssh-agent (like SSH Chain) running so that the scp happens without asking for a password.


Enumerable Constants for Active Record Models

Posted by keith 86 days ago

A problem that had often bothered me in the past is this:

You're modelling a concept in a database like, say, property listings and a property has an attribute of "type". The possible values of "type" are not arbitrary. They are some fixed set of values like "rental", "for sale" and "auction", and the software needs to do stuff depending on the value of "type".

So this is like a :belongs_to where a property belongs to a suburb say. But there is an important difference. With a belongs_to :suburb the values for suburb are arbitrary. That is to say that the software doesn't care about the values, only that there is some value (which the user cares about, of course). But the program does care about the set of values available for the "type" attribute.

Setting up a has_many/belongs_to relationship always seemed overkill but it gave me a nice list of available attributes and I could enforce the relationship. The other problem was that the data in the table would mirror some set of constants in the code. If you changed one, you had to make sure you changed the other or things got out of whack. Not to mention problems of populating a new database and making sure all such "non-arbitrary" value set tables had the correct values (including correct ids to match the constants in code) in them.

Making the attribute "type" a string and stuffing values "rental", "for sale" and "auction" in it felt particularly bad.

Solution

I felt the correct way to do it was to use integer values in the field and represent these values as constants in the code (which is where the values have meaning).

But writing validation for these and coming up with a list of human readable descriptions (for drop-downs) was tedious. It felt like the values belonged in the database... but wait, no, the values have meaning in the code...

So I've written a Rails plugin to take away the tediousness of it all and make it a lot more DRY. With it I get

  • an easy way to define, in a nice DSL looking way, what the constants are
  • validation code generated dynamically
  • a nice way of getting the list of values and descriptions (useful for UI stuff)

Examples

Model definition:

class Property < ActiveRecord::Base
  enumerable_constant :type, :constants => [:rental, :for_sale, :auction]
end

Some view code:

<p><label for="property_type">Property Type</label><br/>
< %= collection_select(:property, :type, Property::Type.constants, :id, :title, {:prompt => 'Select a Property Type'})  % ><br />

Note the use of Property::Type.constants in place of where you would normally use a collection object like @suburbs

Some code that tests the value

property = Property.find :first
if property.type == Property::Type::FOR_SALE
  # do some stuff
end

Other things you can access

>> Property::Type.constants.first.name
=> "RENTAL"
>> Property::Type.constants.first.title
=> "Rental"
>> Property::Type.constants.first.value
=> 1

Download the Enumerable Constants project


Rails Workshop - Sydney

Posted by keith 103 days ago

Last weekend I attended a Rails Workshop in Sydney. The workshop was put together by wwworkshops and presented by Geoffrey Grosenbach of Top Funky Corp.

The workshop was great and I got a lot out of it. Geoffrey gave us a lot of material to wade thru and learn from including code (lots of code), screencasts and cheatsheets.

Geoff is a great presenter. Although Geoff knows a ridiculous amount of stuff, he always comes across as being very approachable willing to learn from those he's presenting to. I like his style.

It was also great to meet up with some talented people in the Sydney Rails community.


Convertible to csv format options

Posted by keith 172 days ago

The latest version of convertible to csv can be told how to format particular fields when it returns data for the to_csv method. The default behaviour is to use the to_s method for each field. But sometimes you might want to display the data a little differently.

Use :format_options to specify a format method to be called when outputting a field. This is useful, for example, if you have a Time field and you want to format it differently to how Time.to_s displays a time.

Here's an example:

class Customer < ActiveRecord::Base
  acts_as_convertible_to_csv :header => true, 
                             :fields => %w(id firstname lastname email_address),
                             :format_options => {:lastname => :format_lastname, :firstname => :format_firstname}
  def format_firstname
    firstname.upcase
  end

  def format_lastname
    lastname.reverse
  end
end

As Customer.find(:all).to_csv is invoked, each time the fields "lastname" and "firstname" are output, instead of calling to_s on the fields, calls are made to the format_firstname and format_lastname methods respectively and your csv output will contain upcased firstname and reversed lastname.


Markdown on Rails

Posted by keith 173 days ago

I have renamed the Markdown Template Handler project to Markdown on Rails. "Markdown Template Handler" was kinda dull and "Markdown on Rails" has a much better ring to it.

If you install the new renamed version you will need to change your config/environemt.rb file:

from this

require 'markdown_template'
ActionView::Base.register_template_handler('md', MarkdownTemplate)
ActionView::Base.register_template_handler('text', MarkdownTemplate)
MarkdownTemplate::map_headings_down_by 0

to

require 'markdown_on_rails'
ActionView::Base.register_template_handler('md', MarkdownOnRails)
ActionView::Base.register_template_handler('text', MarkdownOnRails)
MarkdownOnRails::map_headings_down_by 0

simple as that.


External diff with Subversion using BBEdit

Posted by keith 174 days ago

BBEdit has a great Find Differences function. You get a list of description of differences that you can click on to see the two affected files. You can also do things like apply the difference to either the new or the old file.

Subversion's differences output is of course command-line only, but subversion does support using external diff tools.

Enter bbdiff from John Gruber.

bbdiff is a command line interface to BBEdit's Find Differences command.

Download bbdiff from John Gruber's site, and follow his install instructions (very easy, copy it to where you want it and chmod it to be executable)

You will need to setup subversion to actually use an external diff command. To do this we will need to write a quick interface between the svn diff command and the bbdiff command as the argument lists do not match.

The arguments we want from the svn diff command are $6 (old file) and $7 (new file).

The bbdiff usage states:

Usage: /usr/local/bin/bbdiff [-b -i -s] oldfile newfile
-b keep BBEdit in the background
-i case insensitive comparison
-s ignore leading and trailing whitespace on lines

So we see that we need to pass in params $6 and $7 from the svn diff command in the order that they appear. Write a script that calls bbdiff with those params. Mine is called svndiff2bbdiff:

/usr/local/bin/bbdiff $6 $7

Make sure you chmod your svndiff2bbdiff file to be executable (chmod +x svndiff2bbdiff).

In your ~/.subversion/config file, search for the [helpers] section and create a line like:

diff-cmd = /usr/local/bin/svndiff2bbdiff

assuming your svndiff2bbdiff is in /usr/local/bin/

Test it out: svn diff -r PREV <file_name>.


Convertible to Csv

Posted by keith 174 days ago

Convertible to csv now has a project page.

I've also added some unit tests.

Convertible to csv allows you to specify that an ActiveRecord model should respond to a to_csv message. You can also specify if a header row of field names should be used and which fields (and their order) should be used in the csv output.


Apple iPod / iPhone multi-functional device

Posted by keith 178 days ago
Vote for this article on digg.com: Digg It!

With the upcoming Apple media event on Tuesday 12th September it is widely expected that Apple will introduce an iTunes Movie store, some iPod updates of some kind and the new iMacs (already released at the time of writing). Some pundits are tipping an iPhone.

I'm hoping for an iPod / iPhone multi-functional device.

All the handheld electronic devices I can think of that are wildly successful concentrate on one task and do that task very well. Devices that try to do too many tasks (read: more than one) often do a mediocre job of each task and thus don't turn out to be successful - relatively speaking.

This could be about to change.

For a multi-functional device to be successful, among other things, it would need to do an excellent job of each task it performs and have a UI that accommodates each of its tasks equally well without compromising the usability of any other task.

Unless the two tasks are very closely related in terms of UI requirements, this implies a kind of UI that can morph to fit the requirements of each task.

As UI components such as touch sensitive input devices and small display devices improve, "polymorphic UIs" are much more doable.

Apple Insider thinks that an Apple cell phone is real and ready for production:

"We believe this smart phone has been in development for over 12 months and has overcome substantial challenges including design, interference, battery life and other technical glitches."

The smart phone they speak of is not a multi-functional iPod / iPhone device, just a phone, albeit having an iPod-like form factor:

"The design will be an iPod nano-like candy bar form factor..."

When I think about what I would like Apple to produce in terms of an iPhone, I always think of an iPod nano with which I can make and receive phone calls. ie a multi-functional device. I carry an iPod around so I can listen to music and I carry a phone so I can make phone calls. If some of the UI functions overlap and the difference can be accommodated by a morphing UI, then why not package the two together?

This MacNN article talks about how Apple has filed a "Multi-functional hand-held device patent application"

The article reveals a

"multi-functional hand-held device capable of configuring user inputs based on how the device is to be used."

If the article is reliable, Apple is clearly thinking about multi-functional devices.

And many analysts certainly think Apple is working on a cell phone.

If two groups of analysts got together, one group behind the multi-functional device idea and the other behind the iPhone idea, would they end the meeting with the idea of an iPod / iPhone multi-functional device?

Let's hope it is an iPod / iPhone multi-functional device.

Of course if on Tuesday Apple brings out a multi-functional device that is both an iPod music player and an iPod video player, I'll be both disappointed and looking silly.


Croc man

Posted by keith 183 days ago

It's a sad loss for my home state, my country and the world.

I'll be wearing a croc-man khaki shirt to work tomorrow.

R.I.P croc man.


Markdown template handler for rails

Posted by keith 185 days ago
Vote for this article on digg.com: Digg It!

Writing is all about content. Style and presentation is a separate concern. When I am writing, be it for the web or some documentation or whatever, I like to write pure text. Style and presentation comes later.

Markdown allows me to do that very effectively. I get to concentrate purely on the content of what I am writing and the semantics of the content (this is a heading, this is a list, this should be emphasised...)

For me, this desire to write plain text extends to writing content for web sites. Of course, html has it place but if I am writing a content page for a client's web site, I would much rather just write the content and not have the html markup of that content get in my way.

I solved this by writing a rails markdown template handler so I could write web site content in my rails apps in markdown.

These two images show a rails view (this site's about page) as html and markdown. In the markdown version, the content is free of the clutter of html tags.

The code is released under the MIT licence and can be found at the markdown_template_handler project home


Markdown Syntax Reference

Posted by keith 186 days ago
Vote for this article on digg.com: Digg It!

I have posted a markdown syntax reference. It's mostly for my own benefit but if others can get some use out of it, then great.

I use markdown a lot (read all the time) - this post, this whole site is written in markdown. In my work I often introduce markdown to other programmers and I find myself repeatedly producing pretty much the same sample document to demonstrate the syntax.

Here is the reference


No Database? Sometimes it feels right.

Posted by keith 189 days ago

In No Database, Tim Bray talks about why he doesn't want to use a database for storing comments in a blog system.

Tim says:

...there is a psychology out there in our profession,which says: if you have data that you want to store and retrieve, that means you need a database. But sometimes you don’t. And sometimes you come out ahead on one or both of the less-work and runs-fast metrics by not having one.

I agree with Tim here. I'm not saying no databases, ever! I use databases almost all the time, but there are times when not using a database feels like the right way to do it. It's all a matter of the requirements and what you are modeling.

This blog runs on a system that uses subversion to store it's data. To edit posts I don't need to use a web interface or some client talking to a database. I just edit files.

This gives me the freedom to edit my posts in my favourite text editor from anywhere I like. I just need a working copy of my blog repository. And of course with subversion I can edit offline.

As Tim does, I like the semantics of the Unix filesystem and for writing blog articles, it just feels totally right for me.


Public Key Authentication with SSH

Posted by keith 193 days ago

Here's how to setup public key authentication over ssh.

  1. Generate the keys

    First create a directory on your local machine ~.ssh/ if it is not already there.

    ssh-keygen -t dsa
    ssh-keygen -t rsa
    

    ssh-keygen will ask for the filename to save to. Just press ENTER to use the default values. It will then ask for a passphrase. A blank passphrase can be entered but this is obviously less secure and the same result can be achieved by using an ssh-agent.

    Private and public keys will then be generated. The public keys have a .pub extension.

    Your private keys should be kept private so you should make sure they are only readable by you (chmod 0600).

  2. Copy the Keys

    Now you should copy the public keys to each machine you will want to connect to. The contents of the public key needs to be appended to ~.ssh/authorized_keys on the remote machine. You could upload them using scp then login to the remote machine and append the contents but these commands can do all of that from your local machine:

    cat .ssh/id_dsa.pub | ssh username@newmachine "cat >> .ssh/authorized_keys"
    cat .ssh/id_rsa.pub | ssh username@newmachine "cat >> .ssh/authorized_keys"
    
  3. Configure the SSH daemon

    You need to make sure the SSH daemon (sshd) is configured to allow the use of public keys.

    cd /private/etc/
    sudo vi sshd_config
    

    Make sure the sshd_config file contains these options:

    PubkeyAuthentication yes
    AuthorizedKeysFile .ssh/authorized_keys
    
  4. Test it out:

    ssh username@host
    

    You will now be asked for you passphrase (not your password for the remote machine).

    To avoid having to type in the passphrase, setup an ssh-agent which will effectively do that for you each time you connect to a remote machine that knows about public keys. If you're on a Mac, SSHChain is an excellent utility.


acts_as_convertible_to_csv

Posted by keith 233 days ago
Vote for this article on digg.com: Digg It!

update: convertible to csv now has a project page

A little ruby magic in the form of meta-programming allows you to define your models as being able to output data in csv format. The definition is a DSL style acts_as_convertible_to_csv with :header and :fields as valid specifications.

Here's how you would use it.

In your model declaration:

class Customer < ActiveRecord::Base
  acts_as_convertible_to_csv :header => true, 
                             :fields => %w(id firstname lastname email_address)
end

The first row will contain fields names if you specify :header => true. You can specify which fields and the order in which they appear by using :fields. If you omit :fields, the field list will default to those returned by the human_readable method.

Getting the csv data from a collection of records:

Customer.find(:all).to_csv

You can pass a block:

Customer.find(:all).to_csv do |line|
  # write the line to a file or something
end

You can also convert individual records to csv:

Customer.find(:first).to_csv

The code

class ActiveRecord::Base

  def self.acts_as_convertible_to_csv(*args)

    @@use_header_fields = false

    args.each do |param|
      if param.is_a? Hash
        if param.include? :header and param[:header]
          @@use_header_fields = true
        end

        if param.include? :fields and param[:fields] and param[:fields].is_a? Array
          class_eval <<-CEEND
            def self.csv_header
              %w(#{param[:fields].join(' ')})
            end
          CEEND
        else
          class_eval <<-CEEND
            def self.csv_header
              field_names = Array.new
              self.content_columns.each do |column|
                field_names << column.name
              end
              field_names
            end
          CEEND
        end
      end
    end

    class_eval <<-CEEND
      def self.use_header_fields
        @@use_header_fields
      end

      def to_csv
        line = ""
        self.class.csv_header.each do |field_name|
          line = line + "," + self.send(field_name).to_s
        end
        # remove first ', '
        line.gsub! /^,/, ''
      end
    CEEND
  end

end

# Allow a collection class to repond to to_csv
class Array

  def to_csv(&block)
    data = Array.new
    # check to see if the objects in the array are capable of to_csv    
    if self[0] and self[0].class.method_defined?(:to_csv)
      if self[0].class.use_header_fields
        data << self[0].class.csv_header.join(',')
        yield self[0].class.csv_header.join(',') if block_given?
      end
      each do |object|
        data << object.to_csv
        yield object.to_csv if block_given?
      end
    end
    data
  end

end