Love, Software, and Squeals of Delight


ReviewsSoftwareThoughts

Dock

Software does more than crunch your numbers or take your picture: it can make you angry or bring you joy. And those are the things that we, as developers, have to tune in to if we want to make our products stand out.

Skitch is one of a handful of products I’ve used recently that remind me what makes a product successful. Utility is important, of course, but the love and emotional connection it engenders in its users is where it’s going to live or die.

Let’s start backwards, with the logo, since it’s the first thing you see. Their logo says nothing about their product’s functionality. There’s no initial, no recognizable signifier (thanks for sparing us the paintbrush), or even an abstract swoop. It’s, um, a heart.

And not just any heart, but a hot-pink, shimmering, pumping, overflowing, disco heart.

They’re not saying, “You might like us”. Or even, “As you can see by the ‘S‘, we’re Skitch”. They’re saying, “This is LOVE, baby!”. You and me!!! (How they’re managing to do that without coming across like Mr. Roper, though, is a mystery that is beyond the scope of this post.)

Now, this Love they offer, it’s not too intense. It’s not Valentines Day Red. It’s not a pushy “when are you going to commit” heart, a dreaded “we have to talk” heart, or even an “I’m complicated, but deep” kind of heart. Just hey, hearts, baby, I’m an extrovert, let’s groove on the dance floor and take some snaps!!!

I focus on this because right there in their identity they align themselves not with Software, but with Love and Play — a great gap to bridge. If the product sucked, it would be one thing. But it doesn’t. It makes bold choices and executes them well. They’re going for the whole tamale, not hiding behind industry-speak, and not afraid of a little heart and soul.

So, they have a lot to live up to. Moving beyond the logo, what does Skitch do that works and how can we learn from that?

It’s makes it easy to get started.
I haven’t taken the time to figure out all the features — in fact, I was too ADD to even watch the whole 3 minute video. But I didn’t need to. The critical 2 or 3 features are easy enough to get right away. Once someone commits, they’ll go deeper, but don’t make them wait, fiddle, or read a manual first. Make your learning curve the equivalent of 140 characters or less.

It does the things you’d want it to do.
This may sound simple, but it’s worth repeating. I wanted to take screenshots, write on them in fun fonts with arrows, then upload them to the web. Bingo. No matter how fun an imagined feature is, the trick is matching it to the intuitive use of your product. If people wouldn’t naturally want to do that thing, drop it. It doesn’t matter how slick it is.

It cuts out the middle steps.
In Skitch, you don’t have to select layers, like in Photoshop, to move items that are separate, such as text and arrows. You don’t have to explicitly save the picture on your desktop and then FTP it to the web — the app does it for you in one click. The designers have figured out your starting and ending points, then cut out all the excess steps in between. Spend time here, trimming the middle.

It makes it fun.
It understands that an application is not just about utility: you’re not just tediously trying to upload a picture to a website or share it with a friend — you’re having a moment in your life. Make that moment simple, and when you achieve that, make it fun.

It makes common actions unexpectedly pleasurable to use.
There are tons of patterns we use all the time when interacting with web and desktop applications. For instance, copy and paste. Copy and paste is great and I love doing it, but the folks at Skitch saved me a couple keystrokes by giving me a fun copy button on the image detail page. It wasn’t necessary per se, but it genuinely improved my user experience. So, don’t try to think about new things you could add: find out what the most integral interactions with your product are. Improve those.

Squeals of Delight
That’s what I did when I first used that copy button. That’s what I did when I clicked “webpost”, heard a whoosh, and saw that my screenshot was now on my very own Skitch web page. Go for the squeals: it means, this feels JUST RIGHT. If someone is squealing with delight (for Love or Software!) they are going to come back.

It’s not all paradise and apples of course.

There are confusing and frustrating interactions, just like in any product. These stand out more, ironically, because they raised the bar so high. So Skitchers, as you move forward, solve these problems for me :)

1. How do I get back to my home skitch page from a specific image page? You don’t really mean for me to click on the back button or delete the rest of the URL in the address bar after furrowing my brow looking for the obvious home link, do you?

2. Uploading. From my Skitch app window, I can’t click “webpost” right away because it first shows me “share”. Only after clicking on the “share” is “webpost” exposed. Since clicking “share” triggers a browser window populated with the Share link that steals focus, I have to tab back to Skitch to webpost. Annoying.

Overall, It was great to look at Skitch because it’s so well done. I think it’s worth taking a product apart to see why it works and how we can use those lessons in our own applications.

And finally, Full Disclosure: I have nothing to do with Skitch.

I just heart them.

Don't go splurging at the widget store


Design processIntroductionsThoughts

It is easy for clients, I have noticed, to mistakenly conflate adding widgets, effects and acronyms — Sliders, Sorters, Expanding-Menus, Oh My! — with implementing an idea. The client talks excitedly, rattling off a Rube Goldberg chain of widget-to-widget interactions, their voices rising, the importance of each and every widget in the chain perceived critical to the achievement of the Internet Holy Grail: Angel Investment. Or at least, a really slick site.

Don’t get me wrong. I think everyone is in favor of a well-placed widget.

They can be so smooth and beautiful that you gasp. They can glow yellow for just the correct duration before fading to white (“where has that beautiful apparition gone?”, you wonder, before drunkenly clicking again. And again. And again.). They can add an item to a list almost magically: never was it so fun to have so many things To Do. They can save you clicks, keep you in one place, slide items into carts with almost illicit ease.

In short, they can make things so simple that a tear comes to your eye, and you rush off, hat in hand, in the quest of The Holy Spinner to deliver your payload.

The Holy Spinner

But stop.

What are you looking for? Forget the elevator pitch, as it can be intoxicating: the sound of your voice, people nodding enthusiastically, the doors shut blocking their escape (especially if you are stuck between floors). Instead, do the quiet room test. You alone. Your idea. Naked. A convergence of souls.

“What do you need, Idea”, you ask, “in order to fully manifest your glorious Idea-ness?”

If your idea is quiet, do not rush to speak for it. If your idea speaks but is simple, do not scoff. Do not dress your idea up in Widget Drag, so it looks like a teenager searching for their identity at the Web 2.0 Store. If your idea does not need a Yellow Fade or slider, that is OK. If you remove the slider and yellow fade and find there is no idea underneath, that’s OK, too. Go for a walk. Another idea will come.

Think about building a UI like listening to the ones that you love. You observe them. You listen to their likes and dislikes so your gifts will please them, not reflect your tastes. It’s not about the shiny present: it’s about the connection, the need anticipated and met, a little bit of the edge taken off. Brush cleared, the path made simpler.

If you’re tempted to drive up to your date in the red corvette of ideas — or wow your user with the accordian navigation ’cause it like, opens and closes! — remember that you might be saying more about yourself than anything else.

And then ask yourself: Do you need that rainbow-colored slider on your site?

Good programmers aren't lazy


Thoughts

It goes something like this. A really good programmer, who obviously does not have a lazy cell in his or her brain (the body is another matter entirely!) declares, “I program because I’m too lazy to do the same thing again and again!”.

Variations abound, all coming back to the idea that “laziness” == an aversion to repetition.

Really?

It’s a fallacy, like the skinny girl standing in front of you declaring, “I’m so fat!”. It’s a cry for attention that points out the obvious opposite, and generates a protest, spoken or not. “No, you’re not! You’re (skinny|smart|efficient)!”

Some of the worst code I’ve ever written has been when I’m in genuine lazy mode. Laziness makes you not mind if you copy and paste a function 40 times without abstracting it. Laziness lets you avoid figuring out how to do something better, because you really don’t want to think about it. Laziness lets you put off finding a solution, copy code without understanding it, hard-code values in your applications, or stuff inline css in your html tags ‘just for now’.

Laziness is the ability to tolerate boredom and inefficiency. Good programmers lack that tolerance.

For programmers who still hide behind that phrase, it’s time to drop it. Try something else, like “I am unable to be bored”, “I cannot tolerate repetition”, or “my skin itches and I start squawking when I see DreamWeaver-generated JavaScript”.

Simplicity and elegance are hard. Striving for them is many things, but not lazy.

Using YUI DataTable with Rails


Javascript/AJAXRailsTips and Tricks

I am currently working on an Rails app that integrates the YUI dataTable, and in going through the tutorials I noticed they are all assume a PHP back-end. I also saw a number of people asking how to get this to work with a Rails controller, so I thought I’d write up my experience in the hopes that it helps someone else. For basic info about setting up the dataTable, refer to the YUI site, linked above. I’m also going to try to clarify a few things that I found a bit obscure.

To create a dataTable you have to define a few basic ingredients:

  1. A dataSource. This defines where the info in the table comes from, and what format it is returned in. I’m using JSON.
  2. A schema that you define. This is a part of the dataSource and is essentially a map to your data. The schema tells the table where to find the field values.
  3. An array of column definitions. You supply the name and “key” of each column and any additional information, such as whether it is editable (and if so, which editor to use) and how to format the data inside the column if you don’t want to just spit it out directly.

Let’s start with a dataSource. In this example, we’re making a table of tasks, so I want to hit my tasks controller to return the data. Standard Rails controller action. In the code snippet below,

  1. in line 1, I supply a URL (note that the url ends in .json, and in my controller I have a responds_to block which constructs my json response).
  2. In line 2, I’m telling the dataSource — hey, you’re gonna get some JSON.
  3. And line 3, is where we define the responseSchema, which has two critical parts: the resultsList and the fields.
  var dataSource = new YAHOO.util.XHRDataSource("/projects/13/elements/21/tasks.json");
  dataSource.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;
  dataSource.responseSchema = {
   resultsList: "Resources.data",
   fields: [
      {key:"task.id"},
      {key:"task.status"},
      {key:"task.percent_complete"},
      {key:"task.description"},
      {key:"task.due_date", parser:date}
      ]
  }

Let me be clear since I got hung up on this a bit. You as the application developer are responsible for two things. One, constructing your response — in other words, you can build your JSON response any way you want, with any keys, values and hierarchical structure that make sense (though it will help you to think it through and standardize it, Satyam has some good info on that here). And two, telling your dataSource how to navigate your response: i.e. — where do find what the data you’re looking for (the needle) in the response (the haystack of JSON)? This is the schema, the map of your data.
Controller code that creates my custom JSON:

# tasks_controller.rb
def index
 respond_to do |format|
      format.json{
         tasks = []
         @tasks.each do |task|
           task_container = {}
           task_container  = task
           task_container['editable_by_user'] = permission.edit? # some metadata I use
           task_container['deletable_by_user'] = permission.delete?
           task_container['resource_name'] = "task"
           tasks << task_container
        end
        data = {"Resources" => {"data" => tasks}}
        render :json => data.to_json
      }
  end
end

And just for fun, here’s a look at the JSON my controller gives me back when I hit this url ‘/projects/13/elements/21/tasks.json’.
The JSON:

{"Resources": {"data": [{"task": {"status": "not_started", "started_on": null, "updated_at": "2009-02-07T22
:03:18Z", "project_id": 13, "percent_complete": null, "high_priority": null, "element_id": 32, "deletable_by_user"
: true, "completed_on": null, "editable_by_user": true, "element_title": "looking good", "id": 50, "created_by_id"
: 7, "resource_name": "task", "description": "Pass the stimulus bill", "assignments": [], "due_date"
: "", "users": [], "resource_url": "/projects/13/elements/32/tasks/50", "due": null, "created_at": "2009-02-07T22
:03:18Z"}}, {"task": {"status": "not_started", "started_on": null, "updated_at": "2009-02-07T22:03:39Z"
, "project_id": 13, "percent_complete": null, "high_priority": null, "element_id": 32, "deletable_by_user"
: true, "completed_on": null, "editable_by_user": true, "element_title": "looking good", "id": 51, "created_by_id"
: 7, "resource_name": "task", "description": "Negotiate without pre-conditions", "assignments": [], "due_date"
: "", "users": [], "resource_url": "/projects/13/elements/32/tasks/51", "due": null, "created_at": "2009-02-07T22
:03:39Z"}}]}}

So that’s it! I’ve defined this JSON array and built it, then returned it from the controller when I get a .json request.

Now that you’ve seen the response, take another look at the responseSchema (you created above) and the two properties that you set:

  1. ResultsList. Notice it is set to “Resources.data”, which are the JSON keys I used. It uses dot-syntax to point to array of tasks in my json.
  2. Fields. Again using dot syntax, and having the ResultsList array to navigate through, it can pull the specific values it wants from the list; so ‘task.status’, ‘task.started_on’, etc., will retrieve those values from the response.

Make sense?

You are now one step away from being able to see your table. So, create your column definitions, an array of data about each column in the table. This is also where you can specify formatting and editing information (not covered in this article).

var columnDefinitions = [
  {key:"task.status",formatter:"formatPriority", sortable:true},
  {key:"task.percent_complete", label:"Percent Complete", sortable:true},
  {key:"task.description", label:"Description"},
  {key:"task.due_date", label:"Due Date", editable:true,sortable: "true",formatter:YAHOO.widget.DataTable.formatDate, editor: new YAHOO.widget.DateCellEditor({resource:'task', updateParams:"task[due]"})}
  ];

Notice that each column definition has a key: this key must be accessible in your JSON, AND it must be defined as a field.

And then you create your table. The first argument is the id of a div on the page to which the table will be attached, the last is an optional configuration hash (pagination, anyone?).

var dataTable = new YAHOO.widget.DataTable("project_tasks", columnDefinitions, dataSource, optionalConfigurationHash);

I hope this is helpful to get you started with the YUI dataTable on Rails. I may do further posts on XHR editing of individual cells in a dataTable using Rails.

An Attachment Walked Into A Bar. Was That U, Fu?


DatabaseDesignRails

I recently spent some time working on a Rails application that needed to have various kinds of attachments, such as PDFs and images, for various types of resources, such as Programs and Questions. The app in question lets you create programs and questionnaires for the purpose of granting continuing medical education (CME) credits to doctors — so they’re apprised of the bleeding edge techniques for sawing off your arm or tying your tooth to a doorknob. Not that this matters for the piece, but context — like sugar or salt — makes things go down easier.

If you can picture an office of harried administrators dealing with ever-changing AMA requirements — “This brochure needs to go with this program!”, “This xray picture needs to go with Question 7″, “This program needs to sing ‘Missing You’ while showing you diagrams of a root canal”.. (OK, maybe not the second item), you’ll see why I wanted to build a system that could expand easily. I did not want to find myself in the position of trying to look tough while shamefacedly muttering, “I’m sorry, PDFs can only go with *Programs*, as you initially specified” and watching these not-so-gentle administrative souls stare at me with fully warranted incredulity. Blaming the client is like [fill-in-the-blank]: weirdly fun for about 10 minutes before the hangover sets in.

This project turned into a tour of attachment_fu, single table inheritance, polymorphism and functional testing of uploads. Thanks to input from friends, blogs and the usual Internet suspects, I got it up and running in its first form and decided to write it up. Hopefully what I’ve learned can help someone else. “Link-outs”, the reclusive programmer’s form of the “Shout Out” (aka a list of great resources), can be found at the bottom of the article.

Goal

To implement a system that would allow for many types of files to be uploaded and attached to many types of resources.

Requirements

  1. Many types of assets (image, pdf, etc.)
  2. Assets belong to many types of resources (program, question, etc)
  3. Assets upload to the file system
  4. Assets validate in the context of their parent resource [you upload the pdf in the program form; the image in the question form, etc]
  5. All functional tests pass for uploads

Design

Rather than have database tables for each attachment type (PDFs and images, then eventually MP3s) I made one table called Assets. Listening to my requirements, a friend suggested setting up my Assets model using polymorphism and single table inheritance and that I look into attachment_fu for the uploads. For this exercise I will only be focusing on the PDF upload. Readers can extrapolate from there for image or other media types. This article does not address image resizing, or anything related to uploading images. There are some great links at the bottom that deal with these topics in depth.

Modeling the domain:

Assets (the table) has a field called type that is be [PDF, Image (etc)]; a field called resource_type which is the name of the class it belongs to, [Program, Question, etc.] and resource_id which is the id of the resource it belongs to.

The Asset class will inherit from ActiveRecord. PDF and Image (and any future file types we add to the system) inherit from Asset. Line items from Assets (the table) will look like this (leaving out the other fields for now)

type   | resource_type | resource_id

Pdf      | Program         | 82
Image  | Question         | 79

For my assets, I created the following models:
asset.rb, image.rb and pdf.rb. The latter two inherit from Asset.

# [Asset.rb]
class Asset < ActiveRecord::Base
  belongs_to :resource, :polymorphic=>true
end

# [Pdf.rb]
class Pdf < Asset
  belongs_to :program
  has_attachment  :content_type=>'application/pdf',
                         :storage=>:file_system,
                         :path_prefix=>'public/uploads/pdfs/',
                         :size => 1.megabyte..3.megabytes
  validates_presence_of   :content_type, :filename

   def validate
     if filename &&  /pdf$/.match(filename).nil?
         errors.add(:filename, "must be a PDF ")
     end
  end

end

Because I am using attachment_fu, I need to have a set of basic fields in my table that will handle image and file uploads. The migration I created looks like this:


t.column :parent_id,  :integer     # for the thumbnails of resized images, required for plugin
t.column :type, :string               # for STI on this table (curent types are image, PDF)
t.column :resource_type, :string  # for polymorphism on this table (current types are Program, Question)
t.column :resource_id, :integer    # for polymorphism, (program_id, question_id, etc)
t.column :content_type, :string    # Fields here [content_type] and below all attachment_fu requirements
t.column :filename, :string
t.column :thumbnail, :string
t.column :size, :integer
t.column :width, :integer
t.column :height, :integer

To create the relationship for my parent models (question and program) I set up the polymorphic relationship like so:

# [program.rb]
class Program < ActiveRecord::Base
   has_many    :pdfs, :as=>:resource,  :dependent => :destroy
end

# [question.rb]
class Question < ActiveRecord::Base
  has_many      :images, :as=>:resource, :dependent => :destroy
end

Note the difference between delete and destroy. Destroy will remove the entry from the database and the file from the filesystem. Delete will only delete the database entry.

Once this is set up, you can test it in the console and see the magic of Rails at work. Through the column names and relationships we've built, entries in Assets are automatically created. It's one of the things that's great about Rails. It doesn't simply describe different modeling patterns: it implements them if you set up your code correctly. It's a beautiful thing.

Uploading

Without moving files to the server, the system is not complete. So, onto attachment_fu. The attachment_fu tutorials I found online all made the assumption that the attachment you are uploading is an entity of its own, in its own form: a mugshot, for instance. My case was different: I needed to embed the attachment in the forms for their parent resources (pdf in the program form; image in the question form, etc) rather than independently. So I needed to validate a form for more than one model. In my _form.rhtml partial for program I have the pdf field like so:

# [views/program/_form.rhtml]

< label for="program_notes" >Notes
<%= f.text_area :notes %>

# ... more fields ... < label for="program_status" >Add Brochure? [PDF Files only]< /label > <%= file_field("pdf", "uploaded_data") %>

This means that both a pdf object and a program object are being passed back to the program controller. Since we know that a program has_many pdfs, we can use program.pdfs.build in order to create -- then validate and ultimately save -- pdf objects.

Validating in context of parent resource:

I needed to check for errors with my program fields and my pdf field. This snippet is what I ended up with. The second line, validate_and_build_pdf is just a result of refactoring: it allows the validation methods to be run if you select a file through the upload file field (so if you try to upload a gif, the validation in Pdf.rb will squak) -- but not if you just leave that field empty.

#[programs_controller.rb]
def create
  @program = Program.new(params[:program])
   validate_and_build_pdf(params[:pdf],params[:pdf][:uploaded_data])
   if @program.save
      flash[:notice] = 'Program was successfully created.'
      redirect_to :action => 'list' and return
   else
      render :action => 'new'
   end
end


# this method stops program from saving the asset if the field is blank,
# and returns type-errors if relevant (files not PDF)
def validate_and_build_pdf(fileParams,file)
  if Asset.is_valid_file?(file)
     @pdf = @program.pdfs.build(fileParams)
  end
end

The reason for the Asset.is_valid_file? method was the same: if no file was uploaded an entry was still being made in the assets table due to the polymorphic relationship. So I had to make sure I was dealing with an actual fileobject, not an empty field. Since this would be used across the application for all types of file uploads, I made this a class method of Asset.

[Asset.rb]
# Checks that the object is one of the following types before running validation on it
def self.is_valid_file?(fileObj)
  if fileObj.is_a?(Tempfile)|| fileObj.is_a?(StringIO)
     || (defined?(ActionController::TestUploadedFile) && fileObj.instance_of?(ActionController::TestUploadedFile))
     true
  else
     false
  end
end

You may be wondering what's up with the ActionController::TestUploadedFile condition -- it's because when you run functional tests of uploaded assets, that is what they are -- they are not instances of fileObj.

Once it's determined that you're dealing with a valid file and you hit custom validation methods (such as informing the user that the file needs to be a pdf), you can include them with your program error messages so you only get one messages box at the top of your form, not two:

<%= error_messages_for :program, :pdf %>

Pass Functional Tests

This part is where you test all your uploads from your functional tests. To test uploaded files, you need to put files in your fixtures/files directory then build your tests.

# [programs_controller_test.rb]
def test_create_with_pdf
  fdata = fixture_file_upload('/files/semi.pdf', 'application/pdf')
  num_programs = Program.count
  post :create,   :multipart => true,
  :pdf=>{"uploaded_data"=>fdata},
  :program => {:name=>"Newest Created Program",
                    :is_ongoing=>1,
                    :status=>"Edit"}
  assert_response :redirect
  assert_redirected_to :action => 'list'
  assert_equal num_programs + 1, Program.count
end

I had a gotcha here -- I realized that I has to have the pdf array separate from the program array (since in the form it’s field_for pdf). I fixed it by submitting two objects, program and pdf, to the controller, seen above.

Finally, running the functional testing leaves files on the file system and they should be deleted at the end of all tests. So I added a destroy method in the teardown, which removes them.

def teardown
  Pdf.find(:all).each { |p| p.destroy }
end

Failing Upload Tests
Just to check that only Pdfs can be uploaded for programs, I added a handful of other file types to my directory and created an array of invalid file types in my setup:

# [programs_controller_test.rb]
def setup
 # other stuff....
 @invalid_files = [['lipsum.doc',"application/doc"],['lipsum.rtf',"application/rtf"],
             ['lipsum.txt',"text/plain"],['squiggle.gif','image/gif' ],
             ['squiggle.jpg','image/jpg' ], ['squiggle.png','image/png' ],
             ['squiggle.psd',"image/x-photoshop"],['squiggle.zip', 'application/zip']
             ]
end

and created a test to check these uploads will fail:

  def test_create_with_invalid_file_types
    @invalid_files.each do |name, type|
      fdata = fixture_file_upload('/files/' + name, type)
      post :create,   :multipart => true,
                      :pdf=>{"uploaded_data"=>fdata},
                      :program => {:name=>"Newerest Created Program",
                             :is_ongoing=>1,
                             :status=>"Edit",
                             :start_date=>Time.now.tomorrow,
                             :end_date=>''}
      assert_response 200
    end
  end

This gave me peace of mind that my files are being uploaded, only the right types are accepted, and all is good in the land of PDFs and Programs. And voila, we are done and on to the next task.


Link Outs

Here's a listing of various online resources on the topics covered in this post. This should be enough for a good start. Happy Uploading!