Dr Nic

You! Fill in this form.

Today is the 24th of November.

On this day in history [1]:

1859 – Darwin’s “Origin of Species published”
Darwin’s theory argued that organisms gradually evolve through a process he called natural selection. Even today educated people still can’t cope with the concept that Adam and Eve were monkeys.
1642 – Tasmania discovered
Abel Tasman becomes the first European to discover the island Van Diemen’s Land (later renamed Tasmania). I was born and raised on this island in Australia. Thanks Abel you happy sailor you.
1867 – Barbed wire
Joseph F Glidden patents barbed wire. Centuries later Pamela Anderson makes a movie with the same name. Different date.
1991 – Freddie Mercury dies
Lead singer of rock group Queen, dies of AIDS aged 45.
** 2006 **
You submitted a proposal to talk at Railsconf 2007. I hear you’ll be really good too. Can’t wait to watch.

Go here. Fill in the form.

[1] Data from The History Channel and Wikipedia. Incredibly witty comments are mine.

Supporting JSON callbacks in Rails

Now you know how to write JavaScript widgets, now you need to know how to help others write widgets for your data.

Here’s a short-one act play to describe the issue at hand.

Actors in this play

You: You run a website that you built with Rails. Its got data in it. You’re a hero of the working class, a family man, and own a hybrid car, but you ride a bike to work. You vote, you contribute to the local neighbourhood newspaper, and you love Autumn because the leaves turn pretty colours. You’re not a fool.
Ulrich: Ulrich is a user of your site. Ulrich is a needy, web2.0 super-consumer. Ulrich started using your site when it was first profiled on TechCrunch, Ajaxian, and the Times magazine, but since then he’s found Theo’s website.
Theo: Theo runs a third-party website. Theo’s website has its own data too. It doesn’t have Ulrich’s data. Theo’s site wasn’t written about in TechCrunch, nor Ajaxian, nor even the local church foldover.

[Prelude] Theo just received his third email from Ulrich. Ulrich wants to port his data from Your website to Theo’s. Theo in turn, now has you on Skype…
You: I’m not very happy with the idea of giving up my data.
Theo: What happened to Open APIs? Making the user’s data transferrable with the user?
You: You read too many 37signals blog articles. Even Flickr – web two of all web two sites – doesn’t give up its data.
Theo: But some of my users…
You: You mean Ulrich. He’s emailed me too
Theo: Yeah, Ulrich. Ulrich wants to embed his data from your site into my site.
You: We already have a JSON API..
Theo: Its no good.
You: Works just fine. Returns lovely JavaScript data. Our users use it within our site to write their own widgets. Its one of our big draw cards.
Theo: Its no good. It doesn’t support callbacks.
You: What the?
Theo: Without a callback function there is no way my <script> tags can trigger any functionality when they retrieve the data. Ulrich could include his JavaScript widget into his page on mysite with your data if it was possible to specify a callback function in your API.
You: Instead of just raw JSON data?
Theo: Yeah. Lots of other sites offer it: CoComment, Delicious, Google Data, etc. They all allow callback=myFunction in the URL parameters.
You: [thinking to yourself] Rails don’t support callbacks out of the box. Bugger.
You: Sure, I’ll work on something.
Theo: Thanks, hopefully this gets Ulrich off my back. Should drive us both more traffic too.
You: The wikipedia page for JSON doesn’t mention callbacks.
Theo: Its a wiki. Add it yourself.
You: Smart arse.

The end.

Supporting JSON callbacks in Rails

Piece of cake.

But first, to support pure JSON API data objects from Rails, you have a handy to_json method on the Object class. So, in the partial/view you are rendering, e.g. person_json.rhtml (or person.rjs if you using MinusR plugin), you include:

<%= @object.to_json %>

Yeah, that was tough.

Now, for callback support. Change the above to:

<%= "#{params[:callback]}(" if params[:callback] -%>
<%= @object.to_json -%>
<%= ")" if params[:callback] -%>

Done.

Update: render_json method

Tim Lucas wrote a great article introducing a render_json method to add to your base ApplicationController. It will automatically return JSON data that you pass it, wrapped automatically in a callback or assigned to a variable (or both) if the parameters include a callback or variable value.

Read it, its good.

Debugging Javascript in IE7 – how to clear your Javascript cache

JavaScript first appeared in December 1995 within the Netscape browser.

131 months later and Microsoft latest attempt to provide JavaScript support in its browser is still faulty – deleting all browser files does not delete JavaScript files from your cache.

You try to develop JavaScript for IE7 and with each change you make the browser ignores it. “Delete files…” and refresh page. Nothing. “Delete all…” and refresh page. Nothing. “Delete all…” and restart browser and “Delete all…” and refresh page. Nothing.

A real “fist through the monitor” moment if there ever was one.

If you find this page via Google and you suffer the above pain, then consider this article your Support Group. “My name is Nic, I need JavaScript to work on IE7, and I have a problem.”

So did Daniel. He also had a solution. Good man, Daniel.

So, run the following Ruby script (if you use a different language, can you copy the equivalent into the comments, please, for fellow Support Group members?) to find and delete all JavaScript files in your cache.

files = ENV['USERPROFILE'] + '\\Local Settings\\Temporary Internet Files\\**\\*.js'
Dir[files.gsub(/\\\\/,'/')].each {|file| File.delete file; puts "Deleted: #{file}"}; nil

Create a script for it or just rerun that last line each time you want to clear all JavaScript in the cache.

The Support Group session is over. Coffee and cake is at the front of the room.

DIY widgets – How to embed your site on another site

UPDATE: Demo and tutorial fixed for IE7.

UPDATE: Opera-supporting code.

I used to wonder how Google AdSense worked. How did those ads from over at Google get on another site over here? This article ignores how Google did it and shows you how you can do it. That is, shows you how to embed JavaScript widgets on other people’s websites.

Demonstration

As a demonstration, the following yellow box and text is dynamically generated from a 3rd party web server without using Ajax:

The embedded code

Only one line of code is required to activate the widget to perform the preceding trick:

<script src="http://drnicwilliams.com/wp-content/uploads/2006/11/xss_magic.js"></script>

You can copy that line into any HTML page (even a static file on your computer) and it will generate the same HTML view. Sexy magic.

Cross Site Scripting

The ability for Google and you to embed dynamic content on someone else’s site is called Cross Site Scripting (XSS). If you read the Wikipedia page, the first sentence announces warnings and dire consequences:

Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web applications which allow malicious web users to inject HTML or client-side script into the web pages viewed by other users.

XSS rocks and Wikipedia is wrong

So, Wikipedia and Google differ in their thinking. I’m with Google. XSS rocks. Wikipedia is for 15 year olds who are too lazy to research their own essays.

Its one thing to provide users of your sites with a REST API or include microformats into your HTML, and let them get their dirty hands on your data.

But its another level of mutual lovey-dovey-ness entirely between for users to be able to embed something from your site into their site. Not just images, but dynamic, sex-on-a-stick content. Like Google AdSense, or Sphere (see the “Sphereit” icon at the bottom of any TechCrunch article), or all the various wacky Digg tools (where you can embed “Digg this” etc into your page and look supercool).

How to do this

If you’ve been diligent enough to learn Javascript, you too can do this at home without parental supervision.

Things you’ll need

The following things are lying around on the internet for you to use:

  1. A copy of someone else’s website, say like an article from Ajaxian [1]. This will be called the mock page.
  2. Some data you want to embed into the site. Relax, it will look pretty when the users sees it. Your data must be in JSON format. But I’ll explain this later on.

The run-thru of what will happen

The user will load up the webpage (e.g. Ajaxian mock page) that has a small <script src="http://yoursite.com/magic_xss.js"></script> snippet in it [2]. When the page is loaded, the magic_xss.js file is loaded too. The user doesn’t know nor care.

When the magic_xss.js file is loaded it will do a couple of things:

  1. Install any stylesheets it needs
  2. Insert an empty, invisible HTML element into the page (e.g. <div id="my_magic_xss" />).
  3. Read in any variables (e.g. Google Adsense requires the website owner to specify a number of variables, such as google_ad_format)
  4. Fetch any additional Javascript files or data. This is where even more dynamic magic can be performed. When requesting the additional data, you could pass back the current document’s URL or the current users’s IP address, and the webserver could return data that is relevant to that URL or IP address/geographic location. Clever, eh.
  5. Insert new HTML into the #my_magic_xss element based on the data that is returned from your own server. Your server – not the host website’s server.

XSS is not Ajax

Technically speaking, Ajax uses the XmlHttpRequest object within the browser to fetch content. Unfortunately, in all browsers, it can only fetch content from the same server as the webpage.

That’s why XSS is interesting – we’re fetching data from a 3rd party server. Your server. So, we can’t use XmlHttpRequest.

The tutorial

Hereafter is the tutorial to explain all this. No specific webserver is required for the tutorial – the data returned will be a static file, though you will implement this with your own webapp to be dynamic.

The data

Later on in the tutorial we’ll need some data to represent the dynamic content from your server. For the sake of the demo, you can use my data: http://drnicwilliams.com/wp-content/uploads/2006/11/people_list.js

Remember, in real life this would be a dynamic server request returning dynamic JSON data.

Here’s the JSON we’re going to be using:

MyMagicXss.serverResponse(['Dr Nic', 'Banjo', 'Angus']);

Down the track, this bit of Javascript will be invoked on the browser. It will look for the function MyMagicXss.serverResponse and invoke it with the Array ['Dr Nic', 'Banjo', 'Angus']. The server function would be the same each time. The data would be different.

The embedded javascript

The point of showing you the dynamic data first is to show us that we need 3 things:

  1. A way to fetch and evaluate remote javascript without using XmlHttpRequest
  2. A function MyMagicXss.serverResponse
  3. A place to display these results on the host webpage

These three things are done by the original page that is loaded into the host webpage when the user visits. For our tutorial, this is the xss_magic.js file: http://drnicwilliams.com/wp-content/uploads/2006/11/xss_magic.js

This file looks like:

MyXssMagic = new function() {
  var BASE_URL = 'http://drnicwilliams.com/wp-content/uploads/2006/11/';
  var STYLESHEET = BASE_URL + "xss_magic.css"
  var CONTENT_URL = BASE_URL + 'people_list.js';
  var ROOT = 'my_xss_magic';

  function requestStylesheet(stylesheet_url) {
    stylesheet = document.createElement("link");
    stylesheet.rel = "stylesheet";
    stylesheet.type = "text/css";
    stylesheet.href = stylesheet_url;
    stylesheet.media = "all";
    document.lastChild.firstChild.appendChild(stylesheet);
  }

  function requestContent( local ) {
    var script = document.createElement('script');
    // How you'd pass the current URL into the request
    // script.src = CONTENT_URL + '&url=' + escape(local || location.href);
    script.src = CONTENT_URL;
    // IE7 doesn't like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName('head')[0].appendChild(script);
  }

  this.serverResponse = function( data ) {
    if (!data) return;
    var div = document.getElementById(ROOT);
    var txt = '';
    for (var i = 0; i < data.length; i++) {
      if (txt.length > 0) { txt += ", "; }
      txt += data[i];
    }
    div.innerHTML = "<strong>Names:</strong> " + txt;  // assign new HTML into #ROOT
    div.style.display = 'block'; // make element visible
  }

  requestStylesheet(STYLESHEET);
  document.write("<div id='" + ROOT + "' style='display: none'></div>");
  requestContent();
}

For the most part you should be able to reuse that and modify it as necessary.

No Javascript frameworks allowed

First point – we should not rely upon any Javascript frameworks (e.g. prototype or jquery) as they might conflict with any Javascript already active in the host page. So, our code is encapsulated in an object MyXssMagic which we hope is unique on any host page.

Step one – install stylesheets

  requestStylesheet(STYLESHEET);

So that your embedded HTML looks pretty you might want to preinstall your own stylesheets.

Step two – create the holder element

  document.write("<div id='" + ROOT + "' style='display: none'></div>");

This allows the code to subsequently know where to insert HTML. Here we’re going to install the HTML at the same location where the host inserted our <script src="..."> code.

Step three – request remote data

   requestContent();

which invokes…

  function requestContent( local ) {
    var script = document.createElement('script');
    script.src = CONTENT_URL;
    // IE7 doesn't like this: document.body.appendChild(script);
    // Instead use:
    document.getElementsByTagName('head')[0].appendChild(script);
  }

We’re going to load our data by creating a new <script src="..."> element into the page. We can’t use XmlHttpRequest, so this is all we have. This means that the response must be pure Javascript, and must invoke a callback function so that the browser does something with the data.

Step four – process response

The browser automatically executes the returned data (introduced earlier):

MyMagicXss.serverResponse(['Dr Nic', 'Banjo', 'Angus']);

This invokes our callback function:

  this.serverResponse = function( data ) {
    if (!data) return;
    var div = document.getElementById(ROOT);
    var txt = '';
    for (var i = 0; i < data.length; i++) {
      if (txt.length > 0) { txt += ", "; }
      txt += data[i];
    }
    div.innerHTML = "<strong>Names:</strong> " + txt;  // assign new HTML into #ROOT
    div.style.display = 'block'; // make element visible
  }

This inserts “Names: Dr Nic, Banjo, Angus” into the element #my_magic_xss, as per the demostration at the start of the article.

Magic!

Epilogue – dynamic interactions

Loading dynamic data and adding HTML into a host page is easy, as shown. Supporting user interactions, where you dynamically load new data and show new HTML, is slightly more complicated.

Remember the rule: the returned JSON data must be wrapped in a callback function (e.g. MyMagicXss.serverResponse(…data…)).

[1] I suggest Ajaxian.com to you because I fancy they might give me something free for doing it. Here’s hoping.

[2] This snippet got into the page because the authors of the site embedded it for you. You can’t make them. But copying a bit of text into a website template is common place thanks to Google AdSense etc, so unless your target audience is 1800th century quilting website owners, you should be fine. Write up some instructions, and give them the small chunk of HTML that they need to embed.

Coming home to Brisbane

I left Australia on the 3rd of July last year and flew to India. A day that was also my 1st wedding anniversary. My wife was not 100% impressed that I left without her (she didn’t fly out until 3 days later) and we spent half our anniversary apart.

Fast forward 17 months and we get to visit home for a few months. Just in time to miss the European winter. Just in time for the cricket season, with England and Australia vying for the Ashes. Just in time for a big stash of Christmas presents. Sweet.

Its also very exciting to have a chance to catch up with other people plying their trade with Ruby and Rails on the 4th of December, Brisbane. Or perhaps you want to ply your trade with Ruby and need encouragement. Or perhaps you want to know what all the fuss is about with Ruby and Rails.

Summary: Brisbane Ruby Brigade – 4th of December – Milton, Brisbane