(t) 514 312 4307 (email) hello@giraffesoft.ca

Label Your Checkboxes and Radio Buttons to Ease Your User's Woes

Posted by: François Beausoleil on 2009-04-30

Whenever I build forms, I'm very careful to always label all my fields. After all, it's good for accessibility, right? Well, it turns out it's also good for usability. Very, very good.

I'm getting older, and as I'm getting older, I get lazier. I like things to be bold and easily reachable. Small targets annoy me these days. Let's take for example Campfire's login page:


A screenshot of Campfire's login page

Let's say you login and want to be remembered. Nothing easier, right? Simply click the checkbox. But you're lazy, and there's this huge label right next to it. Go ahead, click the label. What? Nothing happens? You're right: nothing happens. Here's their code:

<dd>
  <input type="checkbox" name="remember" value="1" />
  Remember me
</dd>

Do you see the problem? I do. The label is not a label. It's not attached to the checkbox. For fun, let's count the number of pixels available to click on: 12×12, or 144 pixels.

Compare that to the following image:


Another remember me with the clickable area highlighted.  This shows 325×21, or 6825 pixels.

In this particular image, we have an area that's 325×21, or 6825 pixels available for clicking on. But this picture is a lie. The full width of my browser's window is availble for clicking on.

To be clear, don't do this:

<form action="#" method="get">
  <div class="row">
    <!-- Don't do this! -->
    <p>Name</p>
    <input id="name" name="name"/>
  </div>
</form>

Instead, correctly set the field's id and label's for attributes:

<form action="#" method="get">
  <div class="row">
    <label for="name">Name</label>
    <input id="name" name="name"/>
  </div>
</form>

If you want to know how significant this is, please have a look at a toy page I made: example labelling versus clickable area.

Very important note: I did not select Campfire out of spite. I contacted Campfire's support department and was told that I should simply click the checkbox. To be fair, all other 37signals applications have correctly attached the label to the input. Other applications and websites have the very same problem:

This list was compiled in April 2009, after 20 minutes of searching.

More Information on the LABEL element and its use

Acceptance Test Driven Development (ATDD)

Posted by: James Golick on 2009-03-15

Test Driven Development (TDD) is this idea that you should write a unit test before you write the code. You run the test to make sure that it fails. Then, you implement the code necessary to make it pass. Rinse and repeat until the software is finished.

One of the most popular themes among TDD nay-sayers is the ad-absurdum argument. They don't know what they building. They're agile. So, how can they write their tests first?

As any test-driven developer knows, though, you don't write all your tests first. I know this. But, yet, when I first heard about ATDD, I, too, fell in to this fallacious trap.

So, what is ATDD?

It's rather simple, really. Before you implement a feature (or piece of a feature), you write an acceptance test. Then, you drop down to lower level tests (unit, functional) to TDD the code necessary to make the acceptance test go green.

I've only been ATDD'ing for a short while, but I'm already seeing a huge improvement in the quality of the stuff that I'm putting out. Much in the same way that unit testing can be looked at as a design practice for your code, ATDD can be looked at as a design practice for your high level functionality.

Starting with an acceptance test forces you to think at the level of the user before you think about your code. What is the workflow going to be? What should the user see? What buttons, fields, or other functional elements will be necessary and how will they be labeled? In my experience, these are often questions that are asked after the models and controllers (or equivalent units) are already built for that feature.

Answering those questions first seems to be a great way to better understand what you're building. Those are the only relevant issues in the eyes of your users. The answers to those questions are the application. Everything else, including the unit tests, is just the implementation.

Oh yeah, and a comprehensive set of acceptance tests is pretty useful too.

4 Core Competencies of Great Hackers

Posted by: Daniel Haran on 2009-03-10

At giraffesoft we do a lot of pair-programming collaborative development, both internally and when hired to coach or audit teams. After working closely with dozens of programmers, it's time to draw some conclusions.

Here are 4 traits that appear to be universal amongst great hackers:

Typing over 60 wpm

Thousands of hours spent on IM and IRC make you a faster typist. It's a skill that can be learned in school or with specialized software in 20 to 40 hours. So why are so many programmers glancing at their keyboards for common symbols?

Bringing up typing speed in conversation meets some resistance amongst some programmers. I've tried arguing that the micro-interruptions to your programming can knock you out of your flow and kill your productivity. For those that do not listen, I offer the example of people that google hotmail. It seems noobish, doesn't it?

Owning the command-line

Some developers need a pretty GUI or IDE to wrap every command-line utility. During one audit at a consulting company I saw a programmer wrestle with his IDE for 10 minutes in front of two CTOs. Verdict? His IDE didn't yet have support for the last version of Ruby on Rails, which we needed for specific functionality (ActiveResource). He was stuck.

In contrast, every great hacker we know has a customized environment, and they routinely compose unix commands. Even those stuck on Windows because of corporate policy still run Linux at home or on their servers.

Knowing your editor

Let's not pick editor fights: the only 3 editors we know to be used by great hackers are TextMate, vim and emacs. The most productive hackers will often customize their editor heavily.

We haven't met a single great hacker that relied on an IDE, although we hear they exist.

Reading code: owning your tools

We've noticed more people ignoring documentation and going straight to the source code. It's a great start.

Diving into a large code base to quickly understand what it does is a skill that can be practiced. People do get much faster at it with a little bit of practice. So fast that using a debugger to step through code seems slow by comparison; I've been chided by one hacker for using a debugger. "It's slow and it doesn't help you understand the code." Ouch.

Most hackers we know routinely read the source of plugins and frameworks before using them. By choosing better architected tools it's easier to add new functionality or discover security flaws. They also appear rather nonchalant about modifying framework code. They understand what it does, and feel free to modify it as if it was their code.

But, but...

The list isn't final or perfect, just the result of observations and discussions. A check-list for self-improvement.

I don't pretend to fit the description of "great hacker" that I've offered up: I don't read enough code, haven't done much work customizing my editor and barely know some CLI utilities like awk or sed. While I won't claim that learning these 4 skills will make you a great hacker, it seems unlikely you can become one without them.

How to Use TimelineFu: Building an activity feed for a social application

Posted by: François Beausoleil on 2009-02-26

In this article, I would like to show how to use TimelineFu to build a timeline. This is the kind of list of events that we often see on dashboards, such as on GitHub:

An example of a timeline from GitHub

I will assume you already know your way around Rails, so I won't describe the basic steps in excruciating details.

Let's start by making the application and models (I'm using Rails 2.2.2). This is a social application, with people and relationships between people as the basic entities in the domain.

$ rails myfriends
$ cd myfriends
$ git init
$ echo "*.log"     > log/.gitignore
$ echo "*.sqlite3" > db/.gitignore
$ echo "*"         > tmp/.gitignore
$ git add .
$ git commit --message "Initial commit"
$ script/generate scaffold person name:string
$ script/generate scaffold relationship person_id:integer friend_id:integer
$ rake db:migrate
$ git add .
$ git commit --message "Scaffolded models"

Time to write a minimal set of tests.

class PersonTest < ActiveSupport::TestCase
  should_have_valid_fixtures
  should_require_attributes :name
  should_allow_mass_assignment_of :name
  should_not_allow_mass_assignment_of :relationships, :friends, :relationship_ids, :friend_ids
  should_have_many :relationships
  should_have_many :friends
end

And make those pass:

class Person < ActiveRecord::Base
  validates_presence_of :name
  attr_accessible :name

  has_many :relationships
  has_many :friends, :through => :relationships
end

Turning to relationships:

class RelationshipTest < ActiveSupport::TestCase
  should_have_valid_fixtures
  should_require_attributes :friend_id, :person_id
  should_allow_mass_assignment_of :friend, :person
  should_not_allow_mass_assignment_of :friend_id, :person_id

  should_belong_to :person, :friend
end
class Relationship < ActiveRecord::Base
  belongs_to :person
  belongs_to :friend, :class_name => "Person"

  validates_presence_of :person_id, :friend_id
  attr_accessible :person, :friend
end

All pretty standard stuff, really.

Installation

Installation is pretty straightforward:

$ script/plugin install git://github.com/giraffesoft/timeline_fu.git

TimelineFu comes with a generator that will do the basics for you. The generator is completely optional. See the README for details.

$ script/generate timeline_fu
     exists  db/migrate
     create  db/migrate/20090225144815_create_timeline_events.rb
     create  app/models/timeline_event.rb

Let's see what TimelineFu generated for us:

class CreateTimelineEvents < ActiveRecord::Migration
  def self.up
    create_table :timeline_events do |t|
      t.string   :event_type, :subject_type,  :actor_type,  :secondary_subject_type
      t.integer               :subject_id,    :actor_id,    :secondary_subject_id
      t.timestamps
    end
  end
 
  def self.down
    drop_table :timeline_events
  end
end

class TimelineEvent < ActiveRecord::Base
  belongs_to :actor,              :polymorphic => true
  belongs_to :subject,            :polymorphic => true
  belongs_to :secondary_subject,  :polymorphic => true
end

TimelineFu's glossary / terminology

A basic timeline event looks like this:

François added you as a friend

The actor is the person or thing that did an action. The subject is what was acted against. The secondary subject is supporting documentation about the subject. And the event type is pretty self-explanatory. The example above looks like:

  • Actor: François
  • Subject: You (a Person instance)
  • Secondary Subject: the Relationship instance
  • Event Type: "added you as a friend" (friended)

Another example:

François posted a new comment on "How to use TimelineFu to build timelines"

  • Actor: François
  • Subject: Comment
  • Secondary Subject: the Post
  • Event Type: "posted a new comment" (commented)

Let's begin by writing a failing test.

class RelationshipTest < Test::Unit::TestCase
  context "Adding another person as a friend" do
    setup do
      @francois = person(:francois)
      @james    = person(:james)
      @francois.friends << @james
    end

    should_change "TimelineEvent.count", :by => 1
  end
end

Run and see the test fail. Open up TimelineFu's README and look at how timeline events are created. Hint: it's called #fires.

class Relationship < ActiveRecord::Base
  fires :friended, :on => :create
end

If you were to look at the attributes of the timeline_event as it was created, here's what you would find:

--- !ruby/object:TimelineEvent 
attributes: 
  id: "1"
  event_type: friended
  actor_type: 
  actor_id: 
  subject_type: Relationship
  subject_id: "1"
  secondary_subject_type: 
  secondary_subject_id: 
  created_at: 2009-02-25 15:39:54
  updated_at: 2009-02-25 15:39:54

Notice that actor and secondary subject are nil. This is because we haven't specified any options to the #fires call. Let's remedy the situation with a new set of failing tests:

class RelationshipTest < ActiveSupport::TestCase
  context "Adding another person as a friend" do
    context "the timeline event" do
      setup do
        @event = TimelineEvent.last
      end

      should "set the actor to be the person who created the friendship" do
        assert_equal @francois, @event.actor
      end

      should "set the subject to the relationship" do
        assert_equal @francois.relationships.first, @event.subject
      end

      should "set the secondary subject to the new friend" do
        assert_equal @james, @event.secondary_subject
      end

      should "set the event type to be 'friended'" do
        assert_equal "friended", @event.event_type
      end
    end
  end
end
class Relationship < ActiveRecord::Base
  fires :friended, :on => :create, :actor => :person, :subject => :friend, :secondary_subject => :self
end

It's nice to know when someone friends you, but isn't it even better to know when they don't want you as friends anymore? This way, you'll know not to give them gifts when it's their birthday. A new failing test:

class RelationshipTest < Test::Unit::TestCase
  context "Deleting the relationship" do
    setup do
      relationships(:james_to_francois).destroy
    end

    should_change "TimelineEvent.count", :by => 1

    context "the timeline event" do
      setup do
        @event = TimelineEvent.last
      end

      should "set the actor to the person who destroyed the friendship" do
        assert_equal people(:james), @event.actor
      end

      should "set the subject to the relationship" do
        # It's been deleted...  Oops!
      end

      should "set the secondary subject to the old friend" do
        assert_equal people(:francois), @event.secondary_subject
      end

      should "set the event type to 'unfriended'" do
        assert_equal "unfriended", @event.event_type
      end
    end
  end
end

And the implementation:

class Relationship < ActiveRecord::Base
  fires :unfriended, :on => :destroy, :actor => :person, :secondary_subject => :friend
end

Rendering the events feed

We need a way of getting recent events for any person. Let's analyze what we want to achieve. If someone adds me as a friend, I want that event to appear in my timeline (timeline_events WHERE subject == self). I also want to see events of my friends, such as "James added Mat as a friend" (timeline_events WHERE actor IN (my friends)). Let's write a first failing test:

class PersonTest < ActiveSupport::TestCase
  context "A new person" do
    context "where James friends self" do
      setup do
        people(:james).friends << @person
      end

      should_change "@person.recent_events.count", :by => 1
    end
  end
end

Let's do the simplest thing that could possibly work:

class Person < ActiveRecord::Base
  has_many :recent_events, :as => :subject, :class_name => "TimelineEvent", :order => "timeline_events.created_at DESC"
end

That works just fine. But how do I get to the events of my friends? My friends events are the ones where actor_id is one of my friends. Let's write another failing test:

class PersonTest < ActiveSupport::TestCase
  context "A new person" do
    context "where James friends self" do
      context "where James friends someone else when James is my friend" do
        setup do
          @person.friends << people(:james)
          people(:james).friends << Person.create!(:name => "Daniel")
        end

        should_change "@person.recent_events.count", :to => 2
      end
    end
  end
end

And since we want both where subject = X or actor = X, we must use the :finder_sql option of has_many:

class Person < ActiveRecord::Base
  RECENT_EVENTS_CONDITION = 
    '(subject_id = #{id} AND subject_type = \'Person\')
    OR (actor_type = \'Person\'
    AND actor_id IN (SELECT friend_id
                    FROM relationships
                    WHERE relationships.person_id = #{id}))'
    has_many :recent_events,
      :class_name  => "TimelineEvent",
      :finder_sql  => 'SELECT timeline_events.* FROM timeline_events
                      WHERE ' + RECENT_EVENTS_CONDITION + '
                      ORDER BY timeline_events.created_at DESC',
      :counter_sql => 'SELECT COUNT(*) FROM timeline_events
                      WHERE ' + RECENT_EVENTS_CONDITION
end

Now we turn our attention to the user interface. How do we present this information in a nice way to the user? At giraffesoft, we do not write view tests. Views change too often for the tests to be useful. We do use Cucumber though. Anyway, do the simplest thing that could possibly work:

<%= render_timeline @person.recent_events %>

Since TimelineEvent has a field called event_type, let's use that to render a different partial.

module RenderHelper
  def render_timeline(events)
    events.map do |event|
      render(:partial => "timeline_events/#{event.event_type}", :object => event)
    end.join
  end
end

I cannot use render :partial => events here, because all the events have the same type, namely TimelineEvent. I want to render a different partial depending on the value of event_type.

Introducing the timeline_fu Example App

The code for this article is available on github, as the timeline_fu-example application. It is a sample / starter application for rendering event feeds. Fork away!

[FLOSS Week]: timeline_fu: activity feeds made awesome

Posted by: James Golick on 2009-02-20

This release announcement is part of FLOSS week.

If you're working on a webapp these days, chances are you've needed to build an activity feed or two. There are a few approaches floating around — mostly involving observers. Not a fan of observers and needing something reusable, we built our own.

To get started with timeline_fu, run the generator.

script/generate timeline_fu && rake db:migrate

You'll get a model called TimelineEvent and the necessary migration. Then, anywhere you need to fire a timeline event, simply declare it.

class Post
  belongs_to :creator, :class_name => "User"

  fires :created, :actor => :creator,
                  :on    => :create
end

This will fire a TimelineEvent called :created in an after_create callback on the Post model. It's properties will be as follows.

@post = Post.create(:creator => current_user, ...)

@timeline_event.event_type #=> :created
@timeline_event.actor      #=> @post.creator
@timeline_event.subject    #=> @post

Then, to display the timeline_events, you'll need an association on your User model. We've frequently defined this as a has_many :through followed items, like how you might imagine it's implemented in the github activity feed. Then, in your dashboards/show.html.erb, you'd have something like this.

<% @user.timeline_events.each do |e|  -%>
  <%= render :partial => 
    "dashboards/timeline_events/#{e.event_type}", :object => e %>
<% end %>

That's it! Just add fires declarations wherever you need to add something to the activity feed and the necessary templates and you're good to go.

Get It!

$ sudo gem install giraffesoft-timeline_fu

Or fork it on github.

This release announcement is part of FLOSS week. If you're interested in more open source software, consider subscribing or following us on twitter to get the next announcements asap.

[FLOSS Week]: Cliaws: Easy Command-Line Access to EC2, SQS and S3

Posted by: François Beausoleil on 2009-02-19

This release announcement is part of FLOSS week.

What if you didn't have to find and setup X.509 certificates to quickly use Amazon's EC2, SQS and S3 from your applications? Or from the command-line for that matter? Well, search no more.

$ clis3 list BUCKET
BUCKET/file0
BUCKET/file1
BUCKET/file2

$ clis3 put localfile BUCKET/newfile

# Get a secure URL valid for a couple of hours
$ clis3 url BUCKET/newfile

# Gets the content of the file, unmodified
$ clis3 get BUCKET/newfile

# Gets the contents of the file, but appends a single newline at the end
$ clis3 cat BUCKET/newfile
$ cliec2 list
DNS Name                                           State        Groups
------------------------------------------------------------------------------------------------------------------------
ec2-97-162-23-218.compute-1.amazonaws.com          running      app, web
ec2-73-121-216-98.compute-1.amazonaws.com          running      db
# Create a named queue
$ clisqs create giraffesoft_power
Queue giraffesoft_power was created.

# Push a single message to the queue
$ clisqs push --data "message" giraffesoft_power
Pushed 7 bytes to queue giraffesoft_power

# Retrieve one message from the named queue
$ clisqs pop giraffesoft_power
message

Of course, this library wouldn't be complete if it wasn't also available from your Ruby code.

# Returns an array of the files available in this bucket
Cliaws.s3.list "BUCKET"

# Puts the File, IO stream or String to the named file
Cliaws.s3.put "localfile", "BUCKET/newfile"

# Retrieves the contents from S3
Cliaws.s3.get "BUCKET/newfile"

# Returns an Array of Cliaws::Ec2::Instance objects
Cliaws.ec2.list

# Instances have interesting methods such as #running?

# Push a new message to the named queue
Cliaws.sqs.push "giraffesoft_power", {:adapter => "mysql", :username => "root", :password => "tada!!!"}.to_yaml

# Get said message from the queue
Cliaws.sqs.pop "giraffesoft_power"

Get It!

Installation could not be simpler:

$ gem install cliaws

Cliaws depends on 2 environment variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Set them to the correct values before calling into the library. I have mine setup in my ~/.bashrc.

Fork Cliaws on github.

This release announcement is part of FLOSS week. If you're interested in more open source software, consider subscribing or following us on twitter to get the next announcements asap.

[FLOSS Week]: is_taggable: Tagging That Isn't on Drugs

Posted by: Daniel Haran on 2009-02-18

This release announcement is part of FLOSS week.

At last! A simple tagging implementation.

There are several others — many far more featureful. If you absolutely need a tag cloud and writing one sounds scary, is_taggable may not be for you. At least, not yet.

is_taggable is just over 100 lines of code (plus tests). It's simple and well-tested. So, extending it is easy.

So, why did we write it? We needed support for tag 'kinds'. Let's take our polyglot user example:

class User < ActiveRecord::Base
  is_taggable :tags, :languages
end

User.new :tag_list => "rails, giraffesoft", :language_list => "english, french, spanish, latin, esperanto, tlhIngan Hol"

Of course, it also supports the simplest case:

class Post < ActiveRecord::Base
  is_taggable
end

Post.new :tag_list => 'simple, sane defaults'

Get it

 $ sudo gem install is_taggable 

As a rails gem dependency:

 config.gem 'is_taggable' 

Or get the source from github:

 $ git clone git://github.com/giraffesoft/is_taggable.git 

(or fork it at http://github.com/giraffesoft/is_taggable)

This release announcement is part of FLOSS week. If you're interested in more open source software, consider subscribing or following us on twitter to get the next announcements asap.

[FLOSS Week]: enum_field

Posted by: Mathieu Martin on 2009-02-17

This release announcement is part of FLOSS week.

At GiraffeSoft, we love DRY code. That's why we're very quick at creating small plugins that encapsulate the way we code.

Today I'll unveil one such plugin. It's a very simple one that lets you specify an enum field for an ActiveRecord model.

The plugin of course encapsulates a standard validates_inclusion_of

class Computer < ActiveRecord::Base
  validates_inclusion_of :status, :in =>
    ['on', 'off', 'standby', 'sleep', 'out of this world']
  #...
end

But then it adds a few nifty extras that just make sense. So using the enum_field plugin:

class Computer < ActiveRecord::Base
  enum_field :status,
    ['on', 'off', 'standby', 'sleep', 'out of this world']
  #...
end

gives us a bunch useful things:

  • add a validates_inclusion_of with a simple error message ("invalid #{field}")
  • define the following query methods, in the name of expressive code:
    • on?
    • off?
    • standby?
    • sleep?
    • out_of_this_world?
  • define the Computer::STATUSES constant, which contains all acceptable values

This plugin has been known to work very well with other plugins. For example, we've used it successfully with Phusion's excellent default_value_for plugin.

class Computer < ActiveRecord::Base
  enum_field :status,
    ['on', 'off', 'standby', 'sleep', 'out of this world']
  default_value_for :status, 'off'
  #...
end

If you don't like the default error message, you can of course override it with an additional :message option.

enum_field :status, 
  ['on', 'off', 'standby', 'sleep', 'out of this world'],
  :message => "incorrect status"

Get it

You can install it inside your app either as a plugin:

$ script/plugin install git://github.com/giraffesoft/enum_field.git

or as a gem, add the following line in you config/environment.rb:

config.gem "giraffesoft-enum_field", :lib => "enum_field", 
  :source => "http://gems.github.com"

or of course, fork it on GitHub

Conclusion

This is a good general purpose plugin to include in one's customized Rails app generator. We may add it to blank in the future, if we find we're frequently adding it to new projects.

This release announcement is part of FLOSS week. If you're interested in more open source software, consider subscribing or following us on twitter to get the next announcements asap.

[FLOSS week]: classy_resources - resource_controller for sinatra

Posted by: James Golick on 2009-02-16

This release announcement is part of FLOSS week.

Sinatra is awesome. Lightweight web services have never been easier. But, even sinatra can get a little bit tedious when you have to build three or four full resources for consumption with active resource. Not anymore, though.

Introducing Classy Resources

Classy resources adds a tiny declarative API to sinatra for building active_resource compliant REST APIs. Exposing a model is as simple as:

define_resource :posts, :member     => [:get, :put, :delete],
                        :collection => [:get, :post],
                        :formats    => [:xml, :json, :yaml]

The above declaration gets you a REST API, with all of the actions that active resource expects, in as many formats as you list. You can customize which actions get created by changing the contents of the :member and :collection arrays.

It's also possible to override nearly all of classy_resources' behaviours by simply overriding methods. If you're familiar with resource_controller, you'll recognize this pattern.

For example, if your classes were namespaced in a module, you'd need to override the way that resource names, like :posts, are translated in to classes. It's easy.

def class_for(resource)
  MyModule.const_get(resource.to_s.singularize.classify.constantize)
end

For more overrides, see the README.

Get It

$ sudo gem install giraffesoft-classy_resources

Or fork it at github.

This release announcement is part of FLOSS week. If you're interested in more open source software, consider subscribing or following us on twitter to get the next announcements asap.

FLOSS week

Posted by: James Golick on 2009-02-16

We went over a year without even having a website. Then, we lasted a few months with a website, but no blog. That ends today, hopefully with something of a bang.

At giraffesoft, we're big believers in extracting functionality sooner rather than later. This practice has resulted in a ton of code that we use in all of our projects — nice and DRY.

Only problem is, we never get around to releasing this stuff. Starting a blog seemed like a good excuse to spend some time polishing up some code and, you know, writing READMEs.

So, starting today, we'll be releasing an open source project every day this week. They'll probably be mostly rails plugins. But, you never know. Something else might float in.

If you're interested in following along, subscribe to giraffesoft, the blog. Or, if like me, you don't really use your feed reader anymore, follow @giraffesoft on twitter or github! See you later with the first project.