jlaine.net

Using Rails AJAX Helpers to Create Safe State-changing Links

1235 words · 7 min read

A few months ago there was a heated discussion going on about Google Web Accelerator prefetching links and at the same time wreaking havoc in web apps that used plain GET links to change the state of an application. A few tricks came up on how one could block GWA from accessing given pages, but in the end, using GET requests for operations such as deleting records in your app remained dangerous.

The traditional means to avoid the perils of GWA and friends are two-fold: either use only form buttons (and thus POST requests) to commit these mission-critical actions, or link to a confirmation page that does the same. Unfortunately, these solutions are less than optimal. Using dozens of forms in a web page (think “delete” links in a product listing) makes the code a bit messy and a plethora of delete buttons doesn’t make the page look very nice, either. The problem with a confirmation page is that it adds one more step to the process and thus makes the user think one more time. One part of the beauty of OS X compared to Windows is that it doesn’t try to intervene in every action I make. I like to adhere to the same standards so I want to leave confirmation pages for situations where I really, really think they are crucial.

If you’re using Ruby on Rails to build your next killer web app, consider yourself lucky. In the following paragraphs, I’m going to teach you how to use the übercool AJAX helpers in Rails to create action links that are both slick, accessible and about as safe as you can get in the wild wide web.

OK, let’s assume you have an app ready that uses the following link_to helper call to destroy an item from your collection of sock monkeys:

<macro:code lang="ruby">{=html}
<%= link_to “Delete”, :controller => “monkey”,
:action => “delete”, :id => monkey.id %>
</macro:code>{=html}

This would serve you well, that is, until your uncle Enoch finds an abandoned Google Web Accelerator from the trash bin and your beloved monkeys start evaporating in the thin air. So what’s to the rescue? link_to_remote!

<macro:code lang="ruby">{=html}
<%= link_to_remote “Delete”,
:url => {:controller => “monkey”,
:action => “delete”,
:id => monkey.id},
:update => “monkeys” %>
</macro:code>{=html}

Now we’re talking! You’re from this day on using AJAX in your app. Your monkeys are now destroyed without a refresh of a page, and no GET request is ever made. And as your delete action renders the monkey list when called by AJAX, the list updates itself as if magically. As simple as that. But wait! Aunt Marge (your unpaid tester) yells something behind her AS/400. She can’t use your app, nothing happens in her lynx even though she tries to follow the link. Crap. No javascript.

Fortunately link_to_remote has a fallback system:

<macro:code lang="ruby">{=html}
<%= link_to_remote “Delete”,
{:url => {:controller => “monkey”,
:action => “delete”,
:id => monkey.id},
:update => “monkeys”},
{:href => url_for(:controller => “monkey”,
:action => “delete”,
:id => monkey.id)} %>
</macro:code>{=html}

Using the href parameter makes link_to_remote to include a (tada!) href attribute in the anchor tag it creates. You can use url_for to create the address for it just like in normal links.

Ok, time for a little retrospective. What does our little link widget really do? In normal case, when the user has a modern browser with javascript enabled, when clicked, it calls the delete action with an XMLHttpRequest, and upon success updates the element with id “monkeys” in the current page. If javascript is disabled, the browser will follow the traditional href link to the delete page.

Note that both the AJAX url and the old-fashioned href point to the same action. This is intentional. It gives us the possibility to do pretty powerful things with a tiny little action. We’ll take a look at that action, MonkeyController::delete, next.

<macro:code lang="ruby">{=html}
def delete
if request.xhr?
# … deletion code here …
render :partial => “monkey”, :collection => \@monkeys
elsif request.post?
# … deletion code here …
redirect_to :action => “list”
else # we assume this is a get message
render :action => “delete_confirmation”
end
end
</macro:code>{=html}

The delete function above handles three kinds of requests. If it’s called by AJAX (remember the normal case above?), it deletes the monkey and renders the list of remaining monkeys that will then be displayed in the calling page. If the action is called by a POST request (we’ll get into when this happens in just a few seconds), the monkey is also deleted but this time, the browser is redirected to the original monkey list page with an external redirect. In third case, when this action is called by a GET request, we show the user a confirmation page by rendering them delete_confirmation.rhtml (only the important parts of the template are shown here):

<macro:code lang="ruby">{=html}
<%= form_tag :controller => “monkey”,
:action => “delete” %>
<%= hidden_field_tag “id”, \@params[:id] %>
<%= submit_tag “Really, please get rid of the monkey” %>
<%= end_form_tag %>
</macro:code>{=html}

This is the page shown to old-world users with javascript disabled that clicked on the delete link on the monkey list page, resulting in calling the delete action with a GET request. This page is effectively, as you can see, a form that points to the same old delete method. We need to pass the monkey id in the form and we use the convenient hidden_field_tag helper method for that. Requests from this form page constitute the second case of calling delete, making request.post? return true (because POST is the default method for form_tag).

Why is this method cool?

You can use what kind of text or image links on your pages you ever want to, just like with normal links. That’s got to be nicer than a battery of submit buttons that look all different in different browsers. Even cooler, you don’t have to stuff your users through the tunnel of indifferent confirmation pages. Just one click, and away they go!

But that’s not all, folks! The approach is also accesible. People with text browsers or screen readers (or the paranoid with javascript disabled) will be presented a traditional link followed by (sigh!) a confirmation page, for their own safety.

Why is this method safe?

No critical action is made with a GET request. In the most common case the deletion is made through an XMLHttpRequest, which is a) using POST method and b) launched by javascript so robots or Accelerators or other villains couldn’t even invoke it. The fallback method, on the other hand, uses the traditional confirmation page idea, forcing the user to submit a POST form before the actual deletion is made.

So, here’ya go! A complete system for deleting monkeys. Well, maybe not complete but you get the idea. And all with just a single controller method. And like with all AJAX helpers in Rails, it’s really easy to turn a traditional, link- or form-based approach to full-blown AJAX goodness in a matter of minutes.

Disclaimer: The point of this article is not to teach all the goodies of link_to_remote or other AJAX helpers in Rails. There’s a lot better(AJAX on Rails video) resources for that and I intentionally left the code barebones simple. You’d certainly want to give the user some kind of indication that an AJAX process is underway, for example. Search the Rails wiki and API docs for more info.

False Insecurity

117 words · 1 min read

Consumer Reports has published a new report about internet security among consumers. One of the gists of the account is that Macs are still safer than Windows PC’s:

Macs are safer than Windows PCs for some online hazards. Only 20 percent of Mac owners surveyed reported detecting a virus in the past two years, compared with 66 percent of Windows PC owners.

What’s interesting is that every fifth Mac user in the USA has detected a virus even though there is not a single Mac virus out in the wild at the moment. The jury is still out on what viruses these guys have detected but I’m pretty sure it’s not a Mac virus. Polyconsumption, perhaps.

(via)

Patent Wars—a Doomsday Scenario?

144 words · 1 min read

One of the major arguments of software patent proponents has been that the patents would only be a defensive shield for their owners. The latest patent war between Amazon.com and Cendant shows again how much the claim holds true (from El Reg):

In his defense, Bezos argues that patents are defensive and should never be used, and illustrated this when Amazon.com sued rival bookseller Barnes and Noble for infringing on its notorious One Click patent.
In fact Bezos hates patents so much he has applied for 15 in his own name of which eight have been granted; as patent-watcher TheoDP noticed recently, one Amazon patent was granted after five rejections over four years. And as patent ‘399 shows, he was busy filing before Amazon.com had sold a single book.
Clearly, the world needs more crusaders against patent abuse like Jeff Bezos.

(The whole article)

I Am Not a number Blogger, I'm a Free man Journalist

115 words · 1 min read

After recent events Aaron Schwartz declares he’s no more writing a blog but an online magazine. In accordance, he writes up his new regime with ten theses. The first rule: “Like all major newspapers, there will be no factchecking.” Read the whole hilarious article.

That said, I just got a notice about a bag full of books waiting for me in the local post office. Won’t complain. I can’t right now think of a better thing to do than to retreat to the summer cottage to peruse Cradle to Cradle and The Best Software Writing I (containing pieces by, among others, Aaron, why the lucky stiff, Clay Shirky, Cory Doctorow, John Gruber and Paul Graham).

True Lies

188 words · 1 min read

If you want to build a successful business, you need to get your customers to trust you.

There’s a fairly new recovery drink (for sportsmen, but you knew that), Bioremix, in the Finnish market. It’s made in Finland, they are sponsoring the Finnish national orienteering team and seem to have some scientific research backing up the effects of the drink. But I’m completely turned off every time I read “Fresh lemon taste” in the package. Fresh? Lemon? If I had to describe the taste with one word it would be ‘puke’.

My empirical, absolutely non-scientific, research tells that the people who’ve tasted the drink can be split in two categories: those who’ll never touch it again and those who can drink it but with a grimace. I’ve yet to meet anyone who’d like the taste.

If the manufacturer tells me lies that I can instantly detect, how can I trust their claims about the ‘scientifically proven’ effects. They might or might not be substantial but I will always be very doubtful about them. You might want consider that when fabricating more things in your packaging and marketing materials.

Recruiting

99 words · 1 min read

Joel Spolsky:

Recruiting has to be done at the Bill and Steve level, not at the Gretchen level. No matter how good a recruiter you are, you can’t compensate for working at a company that people don’t want to work for; you can’t compensate for being the target of eight years of fear and loathing from the slashdot community, which very closely overlaps the people you’re trying to recruit, and you can’t compensate for the fact that a company with a market cap of $272 billion just ain’t going to see their stock price go up.

Plus a follow-up.

EyeTV 610 Coming

286 words · 2 min read

When visiting Lars last week I got a glimpse of a Mac Mini turned into a PVR (Personal Video Recorder). We haven’t had a working video recorder since 1999 and although (and because) we almost never have time to watch TV it’d be great to be able to timeshift the few truly interesting broadcasts. I already have a Mac Mini with all the bells and whistles lying in my shelf so all I needed is a TV tuner for it.

The best known tv boxes for Mac are made by Elgato, a German company with close ties to Cupertino (they say). Their most popular model has been EyeTV 200, which powers both David’s and Lars’s living rooms.

Finland is already in the digital age, however, and about to pull the analog plug totally in 2007. Therefore EyeTV 200 would not be an ideal choice for my needs. Elgato is fortunately running in the front field even with digital PVR’s, providing a wide range of products for different DVB (Digital Video Broadcasting) signals. We have cable-tv in our house so EyeTV 610 is the model I chose. It has an interface for a conax card reader and an updatable firmware so it shouldn’t become obsolete too soon. For those of you who know Finnish, here’s a raving review of the 610.

I can’t wait for getting to play with the system after holidays. EyeTV uses a system called tvtv, which is similar to TiVo’s program guide and can be used to time recordings over the internet. The guide is already working in the biggest countries in Europe and should be available in Finland in late summer.

Ha! There’ll soon be one thing less in my 43things list.

Dave and Dave

133 words · 1 min read

This is too funny: Martin Fowler just pointed to a PDF article about Agile Development written by Dave Thomas. The article has a picture of the writer (Thomas) but to me it looked something totally else than the picture of Dave Thomas I’ve had in my head.

I wondered this in #rubyonrails irc channel and some bright lad noticed that the Dave Thomas who’d written the article was not the same Dave Thomas we knew at all. In fact he is an Australian Computer Science professor.

This is a funny coincidence, even more because PragDave is one of the original signers of the Agile Manifesto and the other Dave is the founding director of AgileAlliance.com. Or at least that’s what the article says. Hope they haven’t mixed up the two, I certainly would.

Reboot Woes

387 words · 2 min read

Wednesday

Had a nice (and expensive) dinner in Cafe Ketchup in Copenhagen Tivoli with lots of Danish bloggers and Scoble. The night turned adventurous when I tried to get out of the already closed Tivoli. After banging the gates for a while with some locals a nice bartender let us out through the emergency exit of his bar. Now that’s a good idea for maximizing your daily income: close the area before the restaurants inside it shut down.

Thursday

Building of Basecamp day. The workshop turned out to be as great as rumoured. I can’t say I heard that much completely new stuff (ok, I’ve probably followed 37signals, Basecamp and Rails a bit closer than average) but hearing all that in a single workshop, in such a coherent manner was probably worth the juicy price.

In the evening we joined the pre-reboot party at someone’s own (!) pub. Saw for the first time a lot of online friends and enjoyed the generous free beverages. The evening ended with a dinner in supposedly the oldest restaurant in Copenhagen, Lille Apoteket. Get one of the steaks if you ever stumble upon the place, at least the pepper steak was excellent.

Friday

Keynotes

Doc Searls was really great, you should definitely see him speaking some time. Scoble was, well, a bit unorganized compared to Searls and I didn’t get much more out of his speech than that I should blog. OK, I will.

Calacanis

I have to admit, I wasn’t that familiar with Jason Calacanis before, but he turned out to be a great speaker, too, articulating the power of blogging a lot better than Scbole. Here’s his own post about the talk.

After Calacanis, David Axmark of MySQL fame talked about how to make money with Open Source software. The topic was really up my alley, maybe a bit too much, since I didn’t find anything really new from his speech. Maybe his quite monotonic presentation had something to do with that, too.

The lunch was predated by a talk by Ben Cerveny, an interface designer behind e.g. Flickr and Quake. The presentation on Game theory was a disappointment for me as I had waited quite a bit more. It all just went flying high past me, don’t really know why. Lars offers one explanation with comments from Ben.