Wednesday, June 16, 2010

Busy Button Benevolence

Rails as a lot of different ways of doing almost anything. Some of those are frowned on, others hailed as the 'right way'. Of course, the 'right way' changes depending on who you are speaking to, and to what the latest craze is in the rails community.

Buttons, specifically form submit buttons are no exception. My mission today was to prevent 'double clicks', which are a problem when the submit action takes long to resolve: users grow bored and stress test the button.

Ordinarily, one could simply rely on the rails goodness of :disable_with => 'some value'. However, this happens to break ajax forms. Soo...

After some searching on google and a couple of tries, I tossed all the crap out and went back to basics [people suggested using observers and the like to detect the click. Mission!].

Surely html tags have basic events, like onclick, onmouseover and the like? A quick test showed me that <input..../> indeed responds to onclick. So, all I had to do was wire the onclick to some js to turnoff the button.

Here is when I came up with:
<% form_remote_for @foo, :url => foo_path(@foo)  do |f| %>
<%= f.error_messages %>
<table cellspacing="0" cellpadding="0"
>
<caption>Update a foo <caption>
<%= render :partial => "form", :object => f %>
<tr>
<td colspan="2">
<%= submit_tag "Update", :onclick => "this.disabled=true;" %>

<
/td>
</tr>
</table>
<% end %>


Yup, that simple. It works partly because of how my page reloads/ajax is designed. The app uses a "content" div to flip ajax content. So, when a user hits 'Update' in this case, the div is replaced with a new 'show' partial or a new(same) 'edit' partial. Point is the button is rendered and you don't need to know to re-enable it on error.

Do do this, I use these little treasures of RJS goodness I concocted myself:

_create.rjs

if object.errors.empty?
#the two lines below allow us to call this magic for models associated with other controllers
path = "/#{object.class.name.underscore.pluralize}/"
class_name_underscore = object.class.name.underscore

#create a pass-through variable if none exists
eval "@#{class_name_underscore} = #{object.class}.find(#{object.id})" unless (eval "@#{class_name_underscore}")

page.insert_html :bottom, "list-body", :partial => "#{path}#{class_name_underscore}", :collection => [object]
page.replace_html "detail", :partial => "#{path}show"
page.visual_effect :highlight, "#{class_name_underscore}-#{object.id}", :endcolor=>"#c0c0c0", :restorecolor=>"#c0c0c0", :duration=>3
else
page.replace_html "detail", :partial => "#{path}new"
end
page.replace_html "flasher", :partial => "/flasher"


_update.rjs

if object.errors.empty?
#the two lines below alow us to call this magic for models associated with other controllers
path = "/#{object.class.name.underscore.pluralize}/"
class_name_underscore = object.class.name.underscore

#create a pass-through variable if none exists
eval "@#{class_name_underscore} = #{object.class}.find(#{object.id})" unless (eval "@#{class_name_underscore}")

page.replace "#{class_name_underscore}-#{object.id}", :partial => "#{path}#{class_name_underscore}", :collection => [object]
page.replace_html "detail", :partial => "#{path}show"
page.visual_effect :highlight, "#{class_name_underscore}-#{object.id}", :endcolor => "#c0c0c0", :restorecolor => "#c0c0c0", :duration=>3
else
page.replace_html "detail", :partial => "#{path}edit"
end
page.replace_html "flasher", :partial => "/flasher"
flash.discard


You keep these in app/views/ and forget about them. They do all the heavy lifting ajax in my projects, now that's DRY.

Wednesday, June 2, 2010

Getting bigger

Well, the time is nigh for ShuntYard to start employing drones. This is a good thing, but also rather overwhelming.

In my quest to build ShuntYard into the most kick-ass development house I've even seen, I'm faced with a bit of an existential crisis right out of the gate: should I employ people dumber than myself?

Lets reason this through: fuck no.

If I'm smarted than the people who work for me, it stands to reason I can do their jobs better*. So, if I can do it better, then I can do it faster also. sooo, why am I hiring them?

The short answer is I'm not. But, as with all things, there is a but.

The first mitigating factor here is company culture. I believe that building a successful business is all about the culture. Hire a bunch of depro losers and you will end up with a depro loser company. Hire rockstars and you will probably be in the news for an office shooting, but damn, fame is fame.

At this early stage of ShuntYard's existense, when it's mostly just me, I need to pick people almost as much for their attitude as for their abilities. So, I'm looking for smart, self-motivated, young people, who can grow into what I want/need them to be.

If I go out and hire stagnated experts with their own jaded view of the world, I'll only end up with the kind of company they want, not what I want.

So, even though I'm looking for smart people, I'm looking for people with potential, not necessarily knowledge under the belt.

So my final verdict on this is: Hire people, not skills. Skills can be learned (though dear god, dont hire complete novices, you need to get work done, not play pin-the-algorithm-on-the-noob**)... where was I going with this... oh yeah, duchebags are duchebags for ever, and ShuntYard is only needs the one.

* With in a narrow field of expertise, I don't presume to be a sales drone, for instance. So, for the sake of argument, presume we are talking developers here, people who are supposed to directly take a load off my shoulders.

**hmmmm