Tuesday 29 July 2008

icon_button_to_remote

Following from my earlier post on making icon buttons, I needed to add AJAXy goodness to the edit actions on one of our index pages.... but I still wanted cute little icons to make them look pretty.

So I've updated the code to incorporate an icon_button_to_remote method. Note that icon_button_to has also been modified, but it still depends upon the get_icon_path and fixed_button_to methods from the previous post.

  # generates appropriate image-buttons, given an "icon type" and the usual
  # button options.
  def icon_button_to(icon_type, icon_label, form_opts = nil, button_opts = {})
    unless form_opts.blank?
      button_opts.merge!(:type => 'image', :src => get_icon_path(icon_type), 
                       :alt => icon_label, :title => icon_label)
      the_icon = fixed_button_to(icon_label, form_opts, button_opts)
    else
      # no form was passed in - we want a non-linked icon.
      the_icon = image_tag(get_icon_path(icon_type), :width => 16, :height => '16', 
                       :alt => icon_label, :title => icon_label)
    end
    content_tag 'div', the_icon, :class => 'icon_button'
  end
  # add a remote AJAX-linked icon button
  def icon_button_to_remote(icon_type, icon_label, remote_options = {}, button_options = {})
    link_to_remote(icon_button_to(icon_type, icon_label, nil, button_options), remote_options)
  end

The example would be for an index page with a list of Widgets. Each one has an "edit" button using the code below. The page also has a section for entering a new widget - which would be replaced by the "edit this widget" template when you click on the icon.

<!-- In the template at the top of the list -->
<%= w_path = new_widget_path() # url_for is slow
    icon_button_to_remote(
         :new, 'Add new widget', 
         :update => 'new_widget_div',
         :url => w_path,
         :method => :get,
         :html => {:href => w_path},
         :complete => "Element.hide('edit_widget_div'); 
                          Element.show('new_widget_div'); 
                          new Effect.Highlight('new_widget_div');"
        ) -%>

<!-- and next to each widget -->
<%= ew_path = edit_widget_path(@widget) # url_for is slow
    icon_button_to_remote(
         :edit, 'Edit this widget', 
         :update => 'edit_widget_div',
         :url => ew_path,
         :method => :get,
         :html => {:href => ew_path},
         :complete => "Element.hide('new_widget_div'); 
                          Element.show('edit_widget_div'); 
                          new Effect.Highlight('edit_widget_div');"
        ) -%>

<!-- Later in the template... -->
<div id="new_widget_div">New widget form would go here</div>

<div id="edit_widget_div" style="display:none;">Edit widget form will be populated here</div>

You also have to update your Controller code for the new and edit actions to accept js and respond with no layout - otherwise your div gets populated with the entire edit/new page :P

The best way is to move the meat of the page into a partial (which can also be loaded in the edit.rhtml template) callable from the action thus:

  def edit
    respond_to do |format|
      format.js { return render(:partial => 'edit', :layout => false) }
      format.html # edit.rhtml
    end
  end

Saturday 26 July 2008

Best vs Good enough

I can't remember which I've heard - I think it was "good enough is the enemy of best", but it could as easily be the other way around. It's a bit confusing as I've definitely heard conflicting stories.

We are the best!

From one perspective, you want your new startup to be the best - because, in a lot of ways, that is what will distinguish you from your competitors. The difference in market-share between Google and Yahoo! is an obvious example. So is the difference between Amazon and... whoever comes second after Amazon.

Clearly it pays big to be the best in your niche. You need to be the best. "Good enough" not only won't cut it, but will relegate you as indistinguishable from the swamp of mediocrity in which all the other companies float.

In this sense, "good enough" is the enemy of "best".

Good enough. Ship it!

And yet, you need to *ship*, because if you don't have anything to ship, then the customers will never hear of you. This follows from the principle of "ship early, ship often". It's all too easy to get caught in analysis paralysis, endlessly perfecting what you've got without ever putting it "out there" for real users to get their hands on.

In that respect, "best" is the enemy of "good enough".

So which is it?

As with everything, you need a strategic balance.

You don't need to be best at *everything*. You'll wear yourself out trying. You just need to be best at what you've chosen for your market differentiator. Perhaps you're aiming to provide the best features for power users, or most comprehensible features for beginners, or even the nicest customer service. Whatever you decide on, use that focus to drive your quest for perfection. You can push for the best in this one area and let other areas be "good enough".

If you try to swallow the elephant whole, you'll never get it down. Instead, keep focussed on your core competencies, and don't worry *too* much about perfection in the other areas. You can always make them better in your 2.0 release.

Tuesday 22 July 2008

More thoughts on Rails-doc 2.0

I'd been re-contemplating the Rails-doc 2.0 site and had begun to write a post about the minor annoyances I found, when suddenly up popped a new post by Jeremy on rubycorner.

I guess I'm not the only person whose experience was akin to: "OMG wow! *click* *click* hang on..."

My annoyances from rails-doc 2.0

There's not a lot wrong with rails-doc 2.0, the following are merely annoying.

Click depth

One of the benefits of the previous rails-doc site is that it's all there at your fingertips all the time. Rails has a deep hierarchy and now the only way into it is to search randomly. This can be fun - the auto-prompter is pretty good... as long as you know exactly what you're looking for. But on the old site, if you can't quite recall what you're looking for, you can browse down to the list alphabetically until you find it. With the new site you have to delete your old search and continue to try other names. Search helps, but having a browse-alternative is also helpful.

Another problem with deep click-hierarchies is that you can't get as much information at once. On the original site, all the methods are on the same page - and all you need do is click the anchor or scroll down to them to get a feel for the whole class. You don't have to keep clicking through and back if you want to look at each of the methods.

Searching code

...requires code-sensitive search. If I type "default_error_messages" it means something entirely different to a search with the words "default error messages" somewhere in the body.

version consistency

If I chose version 1.2.6 for the ActiveRecord class, chances are pretty strong that I'll want to keep looking at version 1.2.6 when I click through to the methods... or anywhere else, for that matter. I think there's a strong case for persisting the chosen version.

Is there really a problem?

As you might notice, these are just GUI annoyances not real issues, as such. So is there really a problem?

Jeremy points out that user-added notes are all well and good, but what we need is for the actual real rails-doc to be updated. That isn't going to happen here (at least with the current incarnation). His proposed solution sounds great - and I'll look forward to its release. Perhaps he should team up with the rails-doc.org folks ;)

But has it got anything right?

Jeremy actually stated this succinctly:

"If I’d known that all we needed was a good looking RDoc app, I could’ve fixed the problem a while ago."

Lets face it, we're geeks and we love shiny new toys... Rails being the epitomy of the shiny new thing. So yes, rails-doc 2.0 got one thing absolutely right - it provides a pretty interface that is easy and fun to explore. That's often all it takes folks like us to start contributing - so they got that right.

Monday 21 July 2008

Rails doc 2.0

Finally some decent documentation for rails!

Like a number of rails enthusiats, I read Jeremy's blog post with a bit of trepidation. Mainly about the decline of rails blogging, it also points out the distinct lack of decent documentation for Rails... and firmly points a finger at all of us for not contributing to making it better. The worrying point is the conclusion that rails will decline in popularity due to a lack of available, comprehensive documentation.

Luckily, Rob Anderton has put my fears to rest. Rob's post has pointed me at the new Rails doc project. I've only just begun to investigate the site, but I can already tell it's far more useful than the standard Rails API I've been relying upon.

For one thing, it links the documentation to the version of rails you're using. This alone would have saved me a few hours of annoyance trying to get a method to accept parameters that are only available in a more current version than what's on our production server. :P

It also has a really neat keyword-search interface that prompts you with reasonable guesses, and a way for the community to add to the documentation (yay)!

All in all, it shows a lot of potential. So lets get cracking on building the doco up to a standard that developers in other languages have come to expect.

Sunday 20 July 2008

One of Pradiptas children

Ok, so I put this into a comment on a previous post, but the phenomena has continued to spawn new children, so I thought I'd add to it.

Two days ago I opened my inbox to find it bursting with the results of an epic cascading thread. An overzealous recruiter had made a massive faux pas. He spammed 416 random rails-related folks with a Rails job offer... but put all their email addresses into the CC field.

The list ran the gamut of Rails, from those who'd barely touched it once, through the average rails developers (such as myself) right up to community luminaries such as DHH and Ryan Bates (of RailsCasts fame).

Reverse flash mob

After a few responses using reply-all, and the typical "don't use reply-all" (using reply-all), suddenly the mailing list became self-aware. Despite being accidentally gathered together in one place through an epic mishap, we recognised that we had a common interest... and a captive audience.

Most members of the group took advantage of this to chat about upcoming Rails conventions and what work we all did, and where.

Within two hours a google group (pradiptas-rolodex) was formed. Swiftly following were:

Sadly the wiki page keeps being deleted...

Anyway, it's been fun.

Highlights from the original thread

The post that began it all

I have a couple of Ruby on Rails position, wanted to know if you are
interested?


Max Archie
Technical Recruiter
Prodigus Source

The post that unified us as a community.

> PLEASE TAKE ME OFF THIS CRAZY EMAILING LIST!!!

Please KEEP ME ON THIS CRAZY LIST!!!

You all seem like pretty interesting folk, and perhaps serendipitously
we've all been added to the same ruby on rails marketing schloc. Even
more interesting to me, is that both my friend Harper and I are on it,
so hell they guy at least has good taste.

So in the spirit of fun email lists

Hi, I'm Anders Conbere, I Hack, run a co-working space in Seattle, and
love me some erlang and python.

What do you guys think of starting a google group I was thinking

"Igotfuckedinbanglalore"

~ Anders

P.S. I've thoughtfully removed our spammer from future communication

The best repsonse to the ad

Hi Max,

I am a recruiter who has an opening for a top-tier recruiter such as
yourself.  I need someone who can unwittingly set off the fury of *at least*
400 people, while ignoring all basic email etiquette.  Would you be
interested?   If not, do you know anyone else who is currently looking for
such an opportunity?

Sincerely,
Thanks for the mile long email thread out of freakin nowhere

Best representation of pradipta's mistake in code

PS:  Sorry for crashing anyone's iphone, but I couldn't resist replying.

module Recruiter
   class Email
      def initialize(site = "http://workingwithrails.com/")
          @recipients = Recruiter::EmailHarvester.harvest(site)
      end

      #
      # TODO: Really should change this to do a BCC instead of a TO
      # so 416 people don't get spammed and start an internet phenomenon of
      # replying to this message until the entire internet crashes.
      #
     def send_email(to_recipients, message_body =
File.read("really_bad_generic_email_body.txt") )
        @message = EmailMessage.new
        @message.to = to_recipients
        @message.body = message_body
        Mailer.send(@message)
     end

   end
end

Friday 18 July 2008

Death by powerpoint

Check out this amazing powerpoint presentation* on making amazing powerpoint presentations!

* no, you don't often get to say that.

Thursday 17 July 2008

Experienced Rails developer available - Sydney

Hi all, My contract is finally coming to a conclusion, so I find myself looking around for the next gig. I have a bit over three years full-time commercial experience with Rails, which I've been using commercially since just before it went 1.0. I'm looking for a contract in the Sydney area.

If you're interested, you can email me at home (24hr turnaround).

Tuesday 15 July 2008

Little boxes, made of errors...

The standard styling for error boxes (at least with v1.2.6) looks ugly as per below.

Screenshot - old FieldWithErrors

The red doesn't go all the way around the actual box for form fields. Yuk! This is a result of the field in question going into a span that is styled - not the actual form field. Stylesheet code as below:

.fieldWithErrors {
  border: 2px solid red;
  background-color: red;
  padding: 2px;
  display: inline;
}

To make it look neat and pretty (as in the below image), use the following code instead:

.fieldWithErrors input, .fieldWithErrors textarea, .fieldWithErrors select  {
  border: 2px solid red;
  display: inline;
  padding: 2px;
}
Screenshot - new FieldWithErrors

Monday 14 July 2008

Your customers *do* know what they want

I had a conversation with an old school friend last night; and we had a brief discussion about entrepreneurship as it relates to IT. He said he wanted to get out of the daily grind, and thought it'd be great to come up with a product and sell it. He was a bit leery, still, not sure about whether he could really "make it".

Having read a number of Paul Graham's fantastic articles I thought the idea was great, but I reminded him of how important it was to make something that your customers actually want. At which point he stated: "I speak to my customers all the time and in my experience they don't know what they want"

At the time I didn't answer. I could see he had a point, but it wasn't the one I was trying to make, and the conversation moved on. But now I think about it I think that he was simply wrong.

What question?

When you ask a user what content they want on their site or ask about styling or layout, you'll frequently bump up against a puzzled look and a lot of hemming and hawing as they try to figure it out. They don't have an answer ready for that and they probably didn't think about it before you asked. Often they have no idea what they want.

Many is the project where the requirements change like a fickle breeze as the scope creeps inexorably onward. It's obvious your customers don't know what they want. Right?

or do they? is it possible you're asking them the wrong questions?

What language?

Since I moved into freelancing, I've been studying up a whole lot more on business. One thing I was surprised to learn is that my customers are actually pretty savvy. They know their business, and they know exactly why they came to you to build their website.

Your customer knows they want a website that promotes their brand to get more customers, or to cross-fertilise their customer-base with their other product-lines, or to act as a tool that is a more efficient/less-expensive way of selling their products.

What they want has to do with their *business* goals, not with the specifics of the technology that will be used to achieve that goal. They know exactly what the goal is and whether they have reached it - and it has nothing to do with font-sizes or table layouts, or whether the "order" button should be on the product list-page or on the product's details-page.

If you don't know what your customer wants - it's most likely because you haven't asked them the right questions. But it's not enough to change what you're asking - you also have to change how you're asking it.

Your customer lives in a different world to you. You live in IT-world, but they live in the world of their business. Your customer uses a language that is different to IT language. They speak in the language of business - with market-penetration, conversion-rates and revenue. They don't operate in the realm of splash-pages, JavaScript or three-column-layouts. Business has its own language, its own way of organising and expressing ideas, and its own way of determining whether or not a set of actions will acheive a required result.

More than your job's worth

If you don't speak the language of business, then you'd better learn quick! These are your customers, and you already know they can't speak IT, and don't have the time or patience to learn. So it's up to you to do the translation service.

After all, that's your job. It's why you get paid the "big bucks" right?

It's your job to learn how to ask questions in such a way that your customer understands what you're asking and why. To translate their requirements into IT-jargon, and your questions back into business-speak. To express their choices in such a way that it gives them enough information to actually make the decision that you are asking of them.

Do they want flash or XHTML? does it matter to them if you use table or CSS-layouts?

No - what they want to know is "will this process of online ordering appeal more to my customers than the other, or will it be too difficult to learn?", "will my customers be able to find my products on this website, or will they get confused and I lose a customer?", "how can I use this software to give me better tracking of purchasing so I can promote or cross-promote appropriately?".

Tell them that if you place the "order now" button on the product-listing page, it could speed up the ordering process, at the expense of creating visual clutter. Explain that new users might get confused by this, but that it could aid frequent users to get through the process quicker. It then becomes a business decision: ie "are we aiming this site at new customers, or our returning/frequent customers?" - which is a much more meaningful basis for their decision-making.

So...

Translate your requirement questions into their language. Explain how their choice will impact them and their business goals. Explain the benefits in a way that they will understand. Make sure what you ask them leads back to their goals - not yours, and you'll find that they actually have a very good grasp of what they want.

Thursday 10 July 2008

MySQL hates Timezones

We've started using the TzTime plugin which leverages the Tzinfo gem. It's pretty good. I've adjusted it slightly for my own preferences, but it works pretty well out of the box.

Unfortunately, we ran into some odd issues. Rails suddenly started to find records that it shouldn't. Eg if we were looking for all Event objects that occurred between two given dates, (and expecting to find none) suddenly we were finding one. Which played havoc with our tests :P

The occurred_at date on the Event object was clearly outside of the date-range we were looking at. We even tested to make sure the timezone wasn't stuffing it around and moving it into the date-window.

Eventually we discovered that the to_s(:db) command was printing the date out in a weird format using a T: '2008-07-10T00:50:23+00:00'. This is quite different from the standard MySQL format, which would have it as just: '2008-07-10 00:50:23'

MySQL needs to be set up properly to accept the "T-format" for DateTime objects and ours hasn't been. However, in our case it's much easier to simply extend Rails than to go through the required infrastructure politics to get global database settings changed (which will affect all the other projects in the business).

So I just updated the way that the to_s(:db) format works to always use the 'Y-M-D H:M:S' format as below.

ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:db => '%Y-%m-%d %H:%M:%S')
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(:db => '%Y-%m-%d %H:%M:%S')

This seems to work just fine. It means that we'll need to update this if we ever change away from MySQL - but MySQL seems to be hard-coded into this organisation and seems pretty unlikely.

Note: when I first put this code into config/environment.rb it worked fine until I ran the tests - at which point I immediately got an error: uninitialized constant ActiveSupport. This went away the moment I moved the code below the Rails.initializer code (ie underneath the line that says: Include your application configuration below).

Monday 7 July 2008

Not dead...

I'm not dead - just been In Thailand. I'll post again when I get up to speed.