<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4485741147814538981</id><updated>2011-11-08T15:44:45.040-08:00</updated><category term='test driven programming'/><category term='javascript checkbox delayed action explorer'/><category term='php browser refresh stopping repeats'/><category term='KnowledgeTree Internet Explorer download'/><category term='php viewing files problems header ob_start'/><category term='object independence'/><category term='php genetrated variables'/><category term='ajax threadsafe callback multiple calls framework'/><category term='web frameworks'/><category term='ubuntu linkedin'/><title type='text'>Adventures in Bad Coding</title><subtitle type='html'>"&lt;i&gt;To err is you, man"&lt;br/&gt;
"Learn from the mistakes of others; it's less embarrassing that way&lt;/i&gt;"</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-7147687207047379159</id><published>2009-08-03T23:29:00.000-07:00</published><updated>2009-08-06T22:34:34.732-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu linkedin'/><title type='text'>Ubuntu vs LinkedIn</title><content type='html'>&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;As the full story unfolds, this would definitely qualifies as an adventure, but not one of mine!&lt;/span&gt;&lt;/blockquote&gt;For some time it has bothered me that I can't use LinkedIn.&lt;br /&gt;&lt;br /&gt;... That is to say, I can't use Ubuntu when running Linux.&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;Update: that really should read "I can't use LinkedIn when running Linux", but I do like the effect!&lt;/span&gt;&lt;/blockquote&gt;Oh! I can go to the site and log in. However, any query based request, and things just go to sleep.&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;??? I thought that the web was supposed to be platform neutral!!!&lt;/span&gt;&lt;/blockquote&gt;It is neutral, in theory, although try accessing a site running asp and see how far you get on a non-windows operating system. To even the score, using a Redmond produced browser will give you problems with clever styling (prior to IE 8, at least), and no visibility for any svg images (the latter being the case of a 400lb gorilla squishing a perfectly good standard that most other browsers support reasonably well.)&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;But why? Think 'Silverlight'&lt;/span&gt;&lt;/blockquote&gt;One day, I hope the penny will drop that having a non-neutral net is in nobody's interest! So, to get back to the LinkedIn issue.&lt;br /&gt;&lt;br /&gt;For once, it doesn't appear to have anything to do with Microsoft conspiracy theories. Nobody, at this stage, appears to quite know just what the problem is!&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;I have no solution, but I admire the problem!&lt;/span&gt;&lt;/blockquote&gt;Happily, there does appear to be a workaround: just tweak the network layer so:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 51, 255);"&gt;sudo ifconfig eth0 mtu 1360&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I tried it and ... presto!&lt;br /&gt;&lt;br /&gt;So I'm happy for now! However, I'm not sure which I find more mysterious: the problem itself, or the process that produced the workaround (which you can read more about on &lt;a href="https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/314713"&gt;this forum&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-7147687207047379159?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/7147687207047379159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=7147687207047379159' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/7147687207047379159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/7147687207047379159'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2009/08/ubuntu-vs-linkedin.html' title='Ubuntu vs LinkedIn'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-910170770244451667</id><published>2009-04-23T19:54:00.000-07:00</published><updated>2009-04-23T20:10:30.070-07:00</updated><title type='text'>In The Frame III: Creating a Project</title><content type='html'>&lt;blockquote style="font-style: italic;"&gt;&lt;span style="font-size:85%;"&gt;Sorry if anyone's been waiting, with baited breath, for this instalment. I had intended to post once a week. However, life intrudes on these plans. So, while I will try and stick to a weekly instalment, there will be no guarantees.&lt;/span&gt;&lt;/blockquote&gt;OK We've decided what we want to do. Now, how do we start going about it?&lt;br /&gt;In this post, I will review what happens in the various frameworks when you create a project:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Rails&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$ rails &lt;project&gt;&lt;/project&gt;&lt;/span&gt;Enchant&lt;br /&gt;&lt;br /&gt;This runs a script that generates a ruby project framework for your application under the folder named 'Enchant'&lt;project&gt;. This was the name I chose for this project. Feel free to use something else.&lt;br /&gt;&lt;br /&gt;Before proceeding to look at the results, I would like to comment that I long ago concluded that Tolkien had some sound advice for programmers: 'Do not meddle in the affairs of wizards! They are subtle and quick to lead you down the garden path before abandoning you behind the compost heap with all the fairies'.&lt;br /&gt;&lt;br /&gt;What I mean by that should become clear when I describe the project structure that rails creates:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;project&gt;Enchant/&lt;/project&gt;&lt;/li&gt;&lt;li&gt;    app/&lt;/li&gt;&lt;li&gt;        controllers/&lt;/li&gt;&lt;li&gt;            application.rb&lt;/li&gt;&lt;li&gt;        helpers/&lt;/li&gt;&lt;li&gt;            application_helper.rb&lt;/li&gt;&lt;li&gt;        models/&lt;/li&gt;&lt;li&gt;        views/&lt;/li&gt;&lt;li&gt;            layouts/&lt;/li&gt;&lt;li&gt;    config/&lt;/li&gt;&lt;li&gt;        environments/&lt;/li&gt;&lt;li&gt;            development.rb&lt;/li&gt;&lt;li&gt;            production.rb&lt;/li&gt;&lt;li&gt;            test.rb&lt;/li&gt;&lt;li&gt;        initializers/&lt;/li&gt;&lt;li&gt;            inflections.rb&lt;/li&gt;&lt;li&gt;            mime_types.rb&lt;/li&gt;&lt;li&gt;        boot.rb&lt;/li&gt;&lt;li&gt;        database.yml&lt;/li&gt;&lt;li&gt;        environment.rb&lt;/li&gt;&lt;li&gt;        routes.rb&lt;/li&gt;&lt;li&gt;    db/&lt;/li&gt;&lt;li&gt;    doc/&lt;/li&gt;&lt;li&gt;        API/&lt;/li&gt;&lt;li&gt;        README_FOR_APP&lt;/li&gt;&lt;li&gt;    lib/&lt;/li&gt;&lt;li&gt;        tasks/&lt;/li&gt;&lt;li&gt;    log/&lt;/li&gt;&lt;li&gt;        development.log&lt;/li&gt;&lt;li&gt;        production.log&lt;/li&gt;&lt;li&gt;        server.log&lt;/li&gt;&lt;li&gt;        test.log&lt;/li&gt;&lt;li&gt;    public/&lt;/li&gt;&lt;li&gt;        images/&lt;/li&gt;&lt;li&gt;        javascripts/&lt;/li&gt;&lt;li&gt;        stylesheets/&lt;/li&gt;&lt;li&gt;        404.html&lt;/li&gt;&lt;li&gt;        422.html&lt;/li&gt;&lt;li&gt;        500.html&lt;/li&gt;&lt;li&gt;        dispatch.cgi&lt;/li&gt;&lt;li&gt;        dispatch.rb&lt;/li&gt;&lt;li&gt;        favicon.ico&lt;/li&gt;&lt;li&gt;        index.html&lt;/li&gt;&lt;li&gt;        robots.txt&lt;/li&gt;&lt;li&gt;    script/&lt;/li&gt;&lt;li&gt;        performance/&lt;/li&gt;&lt;li&gt;        process/&lt;/li&gt;&lt;li&gt;        about&lt;/li&gt;&lt;li&gt;        console&lt;/li&gt;&lt;li&gt;        destroy&lt;/li&gt;&lt;li&gt;        generate&lt;/li&gt;&lt;li&gt;        plugin&lt;/li&gt;&lt;li&gt;        runner&lt;/li&gt;&lt;li&gt;        server&lt;/li&gt;&lt;li&gt;    test/&lt;/li&gt;&lt;li&gt;        fixtures/&lt;/li&gt;&lt;li&gt;        functional/&lt;/li&gt;&lt;li&gt;        integration/&lt;/li&gt;&lt;li&gt;        mocks/&lt;/li&gt;&lt;li&gt;            development/&lt;/li&gt;&lt;li&gt;            test/&lt;/li&gt;&lt;li&gt;        unit/&lt;/li&gt;&lt;li&gt;        test_helper.rb&lt;/li&gt;&lt;li&gt;    tmp/&lt;/li&gt;&lt;li&gt;        cache/&lt;/li&gt;&lt;li&gt;        pids/&lt;/li&gt;&lt;li&gt;        sessions/&lt;/li&gt;&lt;li&gt;        sockets/&lt;/li&gt;&lt;li&gt;    vendor/&lt;/li&gt;&lt;li&gt;        actionmailer/&lt;/li&gt;&lt;li&gt;        actionpack/&lt;/li&gt;&lt;li&gt;        activemodel/&lt;/li&gt;&lt;li&gt;        activerecord/&lt;/li&gt;&lt;li&gt;        activeresource/&lt;/li&gt;&lt;li&gt;        activesupport/&lt;/li&gt;&lt;li&gt;        rails/&lt;/li&gt;&lt;li&gt;        railties/&lt;/li&gt;&lt;li&gt;    Rakefile&lt;/li&gt;&lt;li&gt;    README&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Hello? Are you still there? I'm not surprised if you found all that a bit daunting. It is a bit of overhead for a simple 'hello world', isn't it?&lt;br /&gt;&lt;br /&gt;Of course, you don't actually have to know the ins and outs of *all* these directories before you start doing some work, but it serves to show that Wizards, while thay may do a lot of useful things for you, become less useful when you actually start having to do your own work, and try to figure out what it is that they have actually done for you. (Look up 'Guru' as an anti-pattern). In fact, this tree has even more detail than I have shown, but I think the point is made, even without drilling further down into the depths of the directories, api/, public/, and vendor/.&lt;br /&gt;&lt;br /&gt;Anyway, the folders we do need to concentrate on are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;app/: this is where the bulk of the application code will be placed. It is organised into sub-folders in a fairly intuitive way:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;models/: for classes describing the application data models&lt;/li&gt;&lt;li&gt;views/: for classes handling the output displays&lt;/li&gt;&lt;li&gt;controllers/: for classes that handle the action prompts, interrogate the model, and return the appropriate view&lt;/li&gt;&lt;li&gt;helpers/: for classes that assist at the small-scale  &lt;/li&gt;&lt;/ul&gt;&lt;li&gt;script/: this contains a lot of useful utility scripts for maintaining and developing the project. You'll see these in use shortly.&lt;/li&gt;&lt;li&gt;vendor/: for third party software. By 'third party', I would include common code you develop and maintain yourself for use in multiple applications.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Django&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;By comparison, the structure that Django creates is laughably simple:&lt;br /&gt;$ django Enchant&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Enchant/&lt;ul&gt;&lt;li&gt;__init__.py&lt;/li&gt;&lt;li&gt;manage.py&lt;/li&gt;&lt;li&gt;settings.py&lt;/li&gt;&lt;li&gt;urls.py&lt;/li&gt;&lt;li&gt;views.py&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Whether this naive setup allows for good scaling, or is only good for naive applications, remains to be seen.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Cake&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It is not immediately obvious how to set up a Cake project. Certainly, there is no 'Cake' me a baked project command. There does appear to be an underlying assumption that you operate with one cake application per server, so all you have to do is install Cake and get going in the 'apps' directory. The directory structure of cake appears to suggest this, and is similar to  the Rails structure. Guess you have to copy the template to where you want it. (Was I grumbling about Wizards earlier?)&lt;br /&gt;&lt;br /&gt;Next... Doing something visible.&lt;br /&gt;&lt;/project&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-910170770244451667?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/910170770244451667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=910170770244451667' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/910170770244451667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/910170770244451667'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2009/04/in-frame-iii-creating-project.html' title='In The Frame III: Creating a Project'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-590101431850657968</id><published>2009-03-28T12:00:00.000-07:00</published><updated>2009-04-23T19:46:11.408-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web frameworks'/><title type='text'>In the Frame II: Intentions</title><content type='html'>&lt;span style="font-weight: bold;"&gt;The Application:&lt;/span&gt;&lt;br /&gt;Having announced my ambitions, I had better announce my intentions: what application shall I write?&lt;br /&gt;&lt;br /&gt;My trusty Rails reference is Dave Thomas and David Heinemeier Honsson :'&lt;a href="http://www.amazon.com/Agile-Web-Development-Rails-2nd/dp/0977616630/ref=sr_1_2?ie=UTF8&amp;amp;s=books&amp;amp;qid=1238467836&amp;amp;sr=8-2"&gt;Agile Web Programming with Rails&lt;/a&gt;', which describes a simple shopping cart application. A little copycattish to do that again, I think.&lt;br /&gt;&lt;br /&gt;Instead, since I am going to be developing the application in triplicate, I shall choose something that caters for multi-user interaction: I shall create a conversation whiteboard.&lt;br /&gt;&lt;br /&gt;What this will allow is for each version of the application to interact with each other (in theory!).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Important stuff first&lt;/span&gt;&lt;br /&gt;What to call it?&lt;br /&gt;&lt;br /&gt;Well, it's a chat application with multiple varieties, so a starting point of 'N-chat' leads naturally to calling it:&lt;br /&gt;&lt;br /&gt;&lt;div  style="text-align: center;font-family:arial;"&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="color: rgb(51, 204, 255);font-family:verdana;" &gt;Enchant&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Analysis&lt;/span&gt;&lt;br /&gt;How do I imagine it operating?&lt;br /&gt;&lt;br /&gt;Starting with a conversation board, a user can select which of several groups they might like to join.&lt;br /&gt;(There might even be a snatch of overheard conversation to help them decide)&lt;br /&gt;On selecting that conversation, the user then sees a number of views*:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;one for what is typed in&lt;/li&gt;&lt;li&gt;one for the general buzz of conversation&lt;/li&gt;&lt;li&gt;a few extracted streams, associated with user.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Oh yes, they will probably want to be able to leave as well!&lt;br /&gt;&lt;br /&gt;There are any number of refinements that can be added to all this, but I think this will be more than enough for a training exercise (especially if muggins here is going to be doing it in triplicate!)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Design&lt;/span&gt;&lt;br /&gt;OK, what we have already suggests a number of design issues:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;two main displays: the main listing, and the conversation area&lt;/li&gt;&lt;li&gt;A database (which isn't strictly necessary for casual chit-chat forums, but which might prove useful for playback. This poses a small problem in that I don't actually have a database that is accessible online...so we may dispense with that. That would be a pity, since ORM is a major support feature of these frameworks)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Tackling the main listing first, it should handle the following use cases:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;view conversations (default)&lt;/li&gt;&lt;li&gt;create conversation&lt;/li&gt;&lt;li&gt;join existing conversation&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The conversation space should handle the following, at least:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;add to conversation&lt;/li&gt;&lt;li&gt;update conversation&lt;/li&gt;&lt;li&gt;leave conversation&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;In the spirit of Agile programming, we will leave the list at that, and add to it as time and experience suggest.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;*You will note a few mixed metaphors going on here, as I try and cram square pegs into round holes, and describe a visual application for what is, traditionally, an aural/oral activity. This isn't an idle observation: I think it's a major issue of chat sessions, which we will not be solving here)&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-590101431850657968?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/590101431850657968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=590101431850657968' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/590101431850657968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/590101431850657968'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2009/03/in-frame-ii-intentions.html' title='In the Frame II: Intentions'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-7128201664415156001</id><published>2009-03-21T14:00:00.000-07:00</published><updated>2009-03-21T14:00:11.827-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web frameworks'/><title type='text'>In the Frame I: Ambitions</title><content type='html'>After a prolonged absence from this motley collection of tidbits, and faced with a prolonged period of inactivity known as 'unemployment', I have decided to embark on a bit of self-education.&lt;br /&gt;&lt;br /&gt;The focus of my interest is going to be web frameworks, and I shall be describing my efforts at creating an application.&lt;br /&gt;&lt;br /&gt;The trick is, I shall be investigating, not one, but three:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;PHP Cake&lt;/li&gt;&lt;li&gt;Python's Django&lt;/li&gt;&lt;li&gt;Ruby on Rails&lt;/li&gt;&lt;/ul&gt;They all purport to do the same things, and that is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Provide a 'Model-View-Controller' (MVC) architecture for your (web) application&lt;/li&gt;&lt;li&gt;Provide  'Object-Relational Mapping'  (ORM) by which your application can talk to its database.&lt;/li&gt;&lt;/ul&gt;While these are clearly useful and worthy things (after all, a number of people went to the effort of putting these things together, presumably for a reason) I am going to be taking a slightly different tack, and assess their friendliness, both to the seasoned developer, and to the newcomer who has to contemplate the height of the technological 'entry bar' that a proliferation of applications and their associated buzzwords appear to be doing nothing to keep low.&lt;br /&gt;&lt;br /&gt;Tall order? Well, as a former supervisor once said to me, 'In order to develop, you need to exceed your comfort zone.' (It's a pity &lt;a href="http://c2.com/cgi/wiki?GlassWall"&gt;glass wall&lt;/a&gt; wielding recruitment agencies haven't heard that, but that's another story I will not dwell on here.)&lt;br /&gt;&lt;br /&gt;My intention is that I shall be developing an application (the *same* application) using each of these frameworks, and describing how my attempts are going in these entries. How technical all this will get remains to be seen.&lt;br /&gt;&lt;br /&gt;It may launch careers. It may peter out in an ignominious puddle of dazed confusion.&lt;br /&gt;&lt;br /&gt;That is the nature of experiment! Finding out what works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-7128201664415156001?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/7128201664415156001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=7128201664415156001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/7128201664415156001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/7128201664415156001'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2009/03/in-frame-i-ambitions.html' title='In the Frame I: Ambitions'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-1995057804278708892</id><published>2008-02-28T21:44:00.000-08:00</published><updated>2009-03-20T20:11:20.483-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='test driven programming'/><title type='text'>Learning to Love Being Test Driven</title><content type='html'>Modern coders will, no doubt, find this little monologue hopelessly passe and babyish.&lt;br /&gt;&lt;br /&gt;On the other hand, if your roots lie in the 'traditional waterfall' approach to software development, then there's a refrain that sounds nonsensical when said out loud:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;'Test before you code'&lt;/span&gt;&lt;/blockquote&gt;Of course, the waterfall approach has been well and truly superceded in favour of cyclical development and the &lt;a href="http://agilemanifesto.org/"&gt;Agile Manifesto&lt;/a&gt; (although &lt;a href="http://www.waterfall2006.com/" title="This link will reward the careful reader"&gt;some would disagree&lt;/a&gt;, &lt;span style="font-style: italic;"&gt;sotto voce&lt;/span&gt; ;-).&lt;br /&gt;&lt;br /&gt;So, what is weird about the concept? The first thing that occurs to you is that effect precedes cause: if you test first, then you test...what?&lt;br /&gt;&lt;br /&gt;Actually, if you look at the standard mantra (Analyse, Design, Code, Test) you will see that there is actually plenty to test: by the time you are ready to code even a couple of lines, you should have an idea of what you are coding those lines to do. You will some idea about how that code will be invoked. In short, you will have described an interface.&lt;br /&gt;&lt;br /&gt;So, testing an interface sounds less ridiculous, doesn't it? Throw some parameters at it and see what happens. (of course, this assumes you or your client have put some requirements together in the first place so you know what it's meant to do. You have done that, haven't you? Of course you have!)&lt;br /&gt;&lt;br /&gt;To encourage this desirable behaviour, a number of ancillary frameworks that support what is collectively referred to as 'unit  testing' have recently become available. It's not hard to find a plugin for your language. They provide a base test class from which you derive your own suite, and define each test as a method of that class. The wonders of mirroring allows the base class to invoke all methods that fit a pattern like 'test*' (even non-mirroring languages like C++ can join in, although it requires a little more effort from the tester)&lt;br /&gt;&lt;br /&gt;So, the *very* first bit of code you should write is your test cases, in the cheerful expectation that each and every test will report abject failure (although statistically, some random stub is bound to work by sheer fluke. Never mind, nothing is perfect!)&lt;br /&gt;&lt;br /&gt;Actually, if you do this, your code will probably explode spectacularly, since you haven't written the interface stubs yet! Never mind, most unit test frameworks (and compilers) will take this in their stride, and let you know *precisely* what they think of your coding abilities (and common sense!)&lt;br /&gt;&lt;br /&gt;OK. Take 2, you have now defined and written those interfaces, and the tests run; returning a dutiful set of 'F's. Abject failure is a given at this stage. That's good, because there's only one way things can go from here! Off you go! Busily re-writing your code to bring each and every one of those big F's to a full stop.&lt;br /&gt;&lt;br /&gt;And, when your code is such that your test output is a green bar or chain of dots..... then you can rest from your labours, content that you have a set of reproducible results that prove your code meets the requirements, and that you will quickly know if any results subsequently become irreproducible as you start tinkering with the code to extend it, and make it look and work better (a pastime known as 'refactoring').&lt;br /&gt;&lt;br /&gt;Now, while this is all very well, there is actually something else you can take away from all this. As you write your tests, you will occasionally come across an interface that is difficult to test comprehensively. When this happens, you can put your head down and write hundred of tests for every occasion. Or, you can rest your head in your hands, and have a think about why the interface is a tough one.&lt;br /&gt;&lt;br /&gt;It may be that it's trying to do too much (so break it up). It may be that its effects are not immediately observable (so see how you can open things up). The point that may occur to you from this little piece of introspection is that your unit testing is actually encouraging you to think about your coding habits in a new and generally constructive way: the more open and modular your code is, the more readily testable it is. Well tested code can expected to be more robust code.&lt;br /&gt;&lt;br /&gt;Like any infrastructure, it is always beneficial to provide unit testing. The most benefit is derived from installing it as early on in the project as possible. Like most infrastructures, there will be some who perceive this unproductive clutter as time consuming overhead. "Never mind these silly tests! We want to see measurable progress!" (usually measured by lines of code written* or number of features implemented)&lt;br /&gt;&lt;br /&gt;Allow me to introduce such people to the concept of 'throughput', which sets the upper bound of your productivity as the value of goods/features/whathaveyous that have been passed on to the client.&lt;br /&gt;&lt;br /&gt;The value of an untested feature, to a client, is ... zero. So, it doesn't matter how of these you have rattled off in the past week, your net throughput is effectively... zero.&lt;br /&gt;&lt;br /&gt;Or, maybe that would be expressed as 'FFFFFFF Zero'?&lt;br /&gt;&lt;br /&gt;When you think about it, zero Fs is the desired result.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;* I would be profoundly depressed to hear that anyone is still using LOC as a serious measure of progress! But then, there is always something to be depressed about!&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-1995057804278708892?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/1995057804278708892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=1995057804278708892' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/1995057804278708892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/1995057804278708892'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2008/02/learning-to-love-being-test-driven.html' title='Learning to Love Being Test Driven'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-8360832850653315072</id><published>2007-10-29T17:04:00.000-07:00</published><updated>2008-12-10T03:01:34.131-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KnowledgeTree Internet Explorer download'/><title type='text'>Internet Explorer: KT Dyslexia Bug</title><content type='html'>This one's for anyone out there who's developing plugins for the KnowledgeTree Document Storage system. It serves as a placeholder for the following diagnostic image.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_6ak3SeqoKBo/RyZ2agQLMFI/AAAAAAAAAAc/6zhfpCVv6VM/s1600-h/IEDyslexia.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_6ak3SeqoKBo/RyZ2agQLMFI/AAAAAAAAAAc/6zhfpCVv6VM/s400/IEDyslexia.JPG" alt="" id="BLOGGER_PHOTO_ID_5126915423514341458" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Full details of the problem, how to reproduce it, and (most usefully)  the workaround, may be found in this &lt;a href="http://forums.knowledgetree.com/viewtopic.php?p=11547"&gt;KnowledgeTree forum entry&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-8360832850653315072?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/8360832850653315072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=8360832850653315072' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/8360832850653315072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/8360832850653315072'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/10/internet-explorer-kt-dyslexia-bug.html' title='Internet Explorer: KT Dyslexia Bug'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_6ak3SeqoKBo/RyZ2agQLMFI/AAAAAAAAAAc/6zhfpCVv6VM/s72-c/IEDyslexia.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-6929294637895086921</id><published>2007-06-21T18:10:00.000-07:00</published><updated>2007-10-02T16:56:59.406-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ajax threadsafe callback multiple calls framework'/><title type='text'>Fun With AJAX</title><content type='html'>&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;Update: someone has come up with a slightly different solution to the problem discussed (See &lt;a href="http://www.xml.com/cs/user/view/cs_msg/2815"&gt;here&lt;/a&gt;)&lt;br /&gt;Meanwhile...&lt;/span&gt;&lt;/blockquote&gt;... I mean, what other sort of experience would one have with Asynchronous Javascript And Xml?&lt;br /&gt;&lt;br /&gt;Indeed, there's a lot to like about a technique that allows you to update your webpage with a snippet of information retrieved from the server *without* having to reload the whole sorry spectacle. It allows for much greater range of interaction with the user, as well. As I type this blog entry, a small message at the foot of the page is reassuring me that my draft is being stored automatically. How? Presumably a little java applet is being fired at regular intervals, which invokes an XMLhttpRequest (or XMLObject, if you're using IE 6 or earlier), and sends the current data back to the server.&lt;br /&gt;&lt;br /&gt;Well, having eulogized the technique, what have I found to grouch about?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;The Problem&lt;/span&gt;&lt;br /&gt;As an old song put it: 'nice legs, shame about the face!'. One thing, in particular, has been annoying me about the callback mechanism. On just about every tutorial on the subject (and since there are plenty, rather than reinvent the wheel here, I suggest you go google 'Ajax tutorial'! ) you will see something like the following code:&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;var ajaxHandler = false;&lt;br /&gt;&lt;br /&gt;onClick (element)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;try&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Firefox, Opera 8.0+, Safari&lt;br /&gt;ajaxHandler=new XMLHttpRequest();&lt;/div&gt;}&lt;br /&gt;catch (e)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Internet Explorer&lt;br /&gt;try&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;ajaxHandler=new ActiveXObject("Msxml2.XMLHTTP");&lt;/div&gt;}&lt;br /&gt;catch (e)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;try&lt;br /&gt;   {&lt;div style="padding-left: 5mm;"&gt;ajaxHandler=new ActiveXObject("Microsoft.XMLHTTP");&lt;/div&gt;}&lt;br /&gt;   catch (e)&lt;br /&gt;   {&lt;div style="padding-left: 5mm;"&gt;alert("Your browser does not support AJAX!");&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;if (ajaxHandler)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;ajaxHandler.open("GET", "http://www.etc.etc/myurl", true);&lt;br /&gt;ajaxHandler.onreadystatechange = &lt;div style="padding-left: 25mm;"&gt;function()&lt;br /&gt;                                                             {&lt;div style="padding-left: 5mm;"&gt;if (ajaxHandler.readyState == 4)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Handle the reponse!&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;ajaxHandler.send(null);&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Perfectly workable as is, and quite adequate for a simple starting tutorial on how to make an AJAX call.&lt;br /&gt;&lt;br /&gt;Except...&lt;br /&gt;&lt;br /&gt;Except, how does the callback function know where to retrieve the results? It doesn't get provided as a call parameter. Nope! &lt;i&gt;The callback function has to go grubbing around for a global variable!&lt;/i&gt; This is ugly, and I don't just mean in the cosmetic sense either.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;It Gets Worse&lt;/span&gt;&lt;br /&gt;As you gain confidence with javascript and Ajax, you will start to use the 'asynchronous' part of the technique more and more: making several Ajax calls simultaneously. Possibly making several calls to the same Ajax function simultaneously... using the same global object! As anyone familiar with multiple processing will attest, this situation is not just ugly, but downright bad.&lt;br /&gt;&lt;br /&gt;While Quirksmode has an interesting account of what can happen, and how to avoid the worst of it, there is an astonishing paucity of advice on this topic. So, I had a bit of a tinker, and here is my solution.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Preliminary Tidying Up&lt;/span&gt;&lt;br /&gt;First of all, I like a place for everything, and everything in its place. Just as  a server process might create a short lived thread/process to handle each request it receives, I would like to see each Ajax call result handled by a single object.&lt;br /&gt;&lt;br /&gt;If XMlhttpRequest passed itself to the call back function, we would have a solution (and I wouldn't have a topic). Can we simulate this?&lt;br /&gt;&lt;br /&gt;Of course we can, by re-writing the above listing as a wrapper function that implements a chain of command. The main routine then becomes:&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;function MyCallback (handler)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;if (handler.readyState == 4)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Prettier, but!&lt;/div&gt;}&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;function onClick (element)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;CallAjax (MyCallback, "http://www.etc.etc/myurl");&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This separates the code handling AJAX, which now reads as follows.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;var ajaxHandler = AjaxHandler ();&lt;br /&gt;&lt;br /&gt;function AjaxHandler ()&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;try&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Firefox, Opera 8.0+, Safari&lt;br /&gt;handler=new XMLHttpRequest();&lt;/div&gt;}&lt;br /&gt;catch (e)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Internet Explorer&lt;br /&gt;try&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;handler=new ActiveXObject("Msxml2.XMLHTTP");&lt;/div&gt;}&lt;br /&gt;catch (e)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;try&lt;br /&gt;   {&lt;div style="padding-left: 5mm;"&gt;handler=new ActiveXObject("Microsoft.XMLHTTP");&lt;/div&gt;}&lt;br /&gt;   catch (e)&lt;br /&gt;   {&lt;div style="padding-left: 5mm;"&gt;alert("Your browser does not support AJAX!");&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;return handler;&lt;br /&gt;&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;function CallAjax (callback, url)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;if (ajaxHandler)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;ajaxHandler.open("GET", url, true);&lt;br /&gt;ajaxHandler.onreadystatechange = &lt;div style="padding-left: 25mm;"&gt;function()&lt;br /&gt;                                                             {&lt;div style="padding-left: 5mm;"&gt;callback (ajaxHandler);&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;ajaxHandler.send(null);&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;An Obvious Solution...&lt;/span&gt;&lt;br /&gt;Well, this might look a bit tidier but, apart from that, nothing much has been achieved: while the callback function proper now has a handler provided to it, it's still going to be the same handler each time a call is made. We want to create a request object each time we make a call.&lt;br /&gt;&lt;br /&gt;So, would this do?&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;:&lt;br /&gt;&lt;br /&gt;function CallAjax (callback, url)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;var ajaxHandler = AjaxHandler();&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;if (ajaxHandler)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;ajaxHandler.open("GET", url, true);&lt;br /&gt;ajaxHandler.onreadystatechange = &lt;div style="padding-left: 25mm;"&gt;function()&lt;br /&gt;                                                             {&lt;div style="padding-left: 5mm;"&gt;callback (ajaxHandler);&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;ajaxHandler.send(null);&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;...That Doesn't Work!&lt;/span&gt;&lt;br /&gt;Unfortunately, no. The problem here is that, although a new handler object is being created, with its own callback object, the language is java&lt;span style="font-style: italic;"&gt;script, &lt;/span&gt;ie the command is interpreted, so the dummy function is referring to whatever the handler is at the time the callback was received, which probably isn't what you expected!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;A Refinement...&lt;/span&gt;&lt;br /&gt;However, there is a way around this: retain the global handler as an array, and refer to the required handler by its array index:&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;&lt;b&gt;var ajaxHandler = new Array();&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;function AjaxHandler ()&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;:&lt;br /&gt;&lt;b&gt;var handle = ajaxHandler.length;&lt;br /&gt;ajaxHandler[handle] = handler;&lt;br /&gt;return handle;&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;function CallAjax (callback, url)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;var handle = AjaxHandler();&lt;br /&gt;if (handle &gt;= 0)&lt;/b&gt;&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;var handler.open("GET", url, true);&lt;/b&gt;&lt;br /&gt;handler.onreadystatechange = &lt;div style="padding-left: 25mm;"&gt;function()&lt;br /&gt;                                                             {&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;callback (ajaxHandler[handle]);&lt;/b&gt;&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;handler.send(null);&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now we're beginning to cook! Each new request handler is stored in a static array, and the dummy callback function refers to it by its index. This is important because to refer to a handler specifically will cause the same problem as before: several callback functions referring to an indeterminate handler.&lt;br /&gt;&lt;br /&gt;Why this is so has something to do with the way the dummy function is generated (it is a class) . To refer to the handler object itself is to refer to it by reference (ie the address of the temporary script variable) to refer to the array index is to refer to it by value (an integer). The latter approach ensures that each callback method refers to a specific handler.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Commentary&lt;/span&gt;&lt;br /&gt;I haven't been using it long enough to give a definitive judgement but, so far, this technique seems to work well. I do have some qualms about that array of request objects growing in the corner like a stack of dirty dishes. It should get cleared away each time the page is refreshed. However, it may cause memory problems if the page persists for long. I am loath to suggest removing old objects from the array since it would upset the index references of current Ajax calls. If this is a concern, you could replace each object entry with an integer as it completes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;An Embellishment&lt;/span&gt;&lt;br /&gt;One final embellishment to the procedure. Since we now have a single definition of a callback function, we can use it to perform useful universal tasks such as telling whether or not the server side script ran properly. eg, when returning the result as XML, this is readily done by checking to see whether or not the responseXML field was generated. If it wasn't, then the raw text may contain the warning diagnostics. Similar results can be achieved by searching the result Text field for error indicators.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;&lt;br /&gt;function CallAjax (callback, url)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;var handle = AjaxHandler();if (handle &gt;= 0)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;var handler.open("GET", url, true);&lt;br /&gt;handler.onreadystatechange = &lt;div style="padding-left: 25mm;"&gt;function()&lt;br /&gt;                                                             {&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;var response = ajaxHandler[handle];&lt;br /&gt;if (response.responseXML)&lt;br /&gt;{&lt;/b&gt;&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;callback (response );&lt;/b&gt;&lt;/div&gt;&lt;b&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;/b&gt;&lt;div style="padding-left: 5mm;"&gt;&lt;b&gt;alert (response.responseText);&lt;/b&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;handler.send(null);&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;OK? Now go and play.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Final Listing&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;The following is the complete final listing:&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="border: 1px solid black; padding: 5mm; font-family: courier new; font-size: 78%;"&gt;var ajaxHandler = new Array();&lt;br /&gt;&lt;br /&gt;function AjaxHandler ()&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;try&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Firefox, Opera 8.0+, Safari&lt;br /&gt;handler=new XMLHttpRequest();&lt;/div&gt;}&lt;br /&gt;catch (e)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;// Internet Explorer&lt;br /&gt;try&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;handler=new ActiveXObject("Msxml2.XMLHTTP");&lt;/div&gt;}&lt;br /&gt;catch (e)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;try&lt;br /&gt;   {&lt;div style="padding-left: 5mm;"&gt;handler=new ActiveXObject("Microsoft.XMLHTTP");&lt;/div&gt;}&lt;br /&gt;   catch (e)&lt;br /&gt;   {&lt;div style="padding-left: 5mm;"&gt;alert("Your browser does not support AJAX!");&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;var handle = ajaxHandler.length;&lt;br /&gt;ajaxHandler[handle] = handler;&lt;br /&gt;return handle;&lt;/div&gt;}&lt;br /&gt;&lt;br /&gt;function CallAjax (callback, url)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;var handle = AjaxHandler();&lt;br /&gt;&lt;br /&gt;if (handle &gt;= 0)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;var handler = ajaxHandler[handle];&lt;br /&gt;handler.open("GET", url, true);&lt;br /&gt;handler.onreadystatechange = &lt;div style="padding-left: 25mm;"&gt;function()&lt;br /&gt;                                                             {&lt;div style="padding-left: 5mm;"&gt;var response= ajaxHandler[handle];&lt;br /&gt;if (response.responseXML)&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;callback (response);&lt;/div&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;div style="padding-left: 5mm;"&gt;alert (response.responseText);&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;br /&gt;handler.send(null);&lt;br /&gt;&lt;/div&gt;}&lt;/div&gt;}&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-6929294637895086921?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/6929294637895086921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=6929294637895086921' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/6929294637895086921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/6929294637895086921'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/06/fun-with-ajax.html' title='Fun With AJAX'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-3177651247400084055</id><published>2007-05-30T18:24:00.000-07:00</published><updated>2007-05-30T18:44:28.781-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php browser refresh stopping repeats'/><title type='text'>Over Refresh</title><content type='html'>Refresh is a nice feature on browsers to use when you've gone too far, or have updated the style in your page.&lt;br /&gt;&lt;br /&gt;However, it can bite you on occasion.&lt;br /&gt;&lt;br /&gt;What happens when you refresh a screen is that the browser resubmits the previous screen, and shows the result. This can cause a problem when the world has moved on. Such as when you've just clicked 'Add' to enter a new row of data to your database. On such occasions, the add request will be repeated, and a new and duplicate row will be added.&lt;br /&gt;&lt;br /&gt;The first approach to fixing this problem might be to store the newly added row id in the form. If 'Add' has just occurred, and this id exists, then ignore the call.&lt;br /&gt;&lt;br /&gt;...doesn't work. Remember, the refresh call is sending a cached set of post data. This will include the &lt;span style="font-style: italic;"&gt;old &lt;/span&gt;values of any state flags you set to try and avoid the problem. What was right last time, therefore , will be right this time and...&lt;br /&gt;&lt;br /&gt;The only solution to this that I have found is to store your state variables as a server side 'session' variable. Now, when your cached post data is received, it is checked against the &lt;span style="font-style: italic;"&gt;current&lt;/span&gt; session state, which will have stored the fact that you just did an add with this data.&lt;br /&gt;&lt;br /&gt;As someone once said: 'Life wasn't meant to be easy'&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-3177651247400084055?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/3177651247400084055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=3177651247400084055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/3177651247400084055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/3177651247400084055'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/05/over-refresh.html' title='Over Refresh'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-5205261039154022145</id><published>2007-05-20T21:08:00.000-07:00</published><updated>2007-05-20T21:23:16.609-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php genetrated variables'/><title type='text'>Spaced out variables</title><content type='html'>As Jane Austen might have put it:&lt;br /&gt;&lt;blockquote&gt;&lt;font size="2"&gt;'It is a truth universally acknowledged that a young variable name in possession of a whitespace  must be want of a bug correction report.'&lt;/font&gt;&lt;/blockquote&gt;It's a no-brainer. Something no programmer would contemplate doing, even in their most caffeine-crazed moments. So, when I got a complaint about an option which wouldn't work for a particular entry, I did quite a bit of head scratching and tracing before I spotted the problem.&lt;br /&gt;&lt;br /&gt;The option in question was a piece  of html representing a checkbox. It was generated via php as part of a report. Its value was taken from a database table, and was used to generate a variable.&lt;br /&gt;&lt;br /&gt;This one entry was a reference code which just happened to have a space in it...&lt;br /&gt;&lt;br /&gt;A case of what you don't know can hurt you!&lt;br /&gt;&lt;br /&gt;Oops!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-5205261039154022145?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/5205261039154022145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=5205261039154022145' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/5205261039154022145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/5205261039154022145'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/05/spaced-out-variables.html' title='Spaced out variables'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-6014087560560778904</id><published>2007-04-13T00:28:00.000-07:00</published><updated>2007-04-13T07:28:40.250-07:00</updated><title type='text'>The Clayton's #if*</title><content type='html'>Users of C++ will be familiar with the use of the pragma directive '#if' to optionally compile a section of code or, indeed, switch between two sections of code when debugging.&lt;br /&gt;&lt;br /&gt;I've found an interesting little trick that allows you to achieve a similar effect using comments, so long as the language you're using supports both the line ('//') and block ('/*..*/') styles. The trick comes from how these two styles interact with each other.&lt;br /&gt;&lt;br /&gt;Let's start by considering a simple 2 line statement block that has been commented out:&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:courier new;font-size:85%;"  &gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="color: rgb(192, 192, 192);font-family:courier new;font-size:85%;"  &gt;/*&lt;/span&gt;&lt;span style="color: rgb(192, 192, 192);font-size:85%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(192, 192, 192);font-family:courier new;font-size:85%;"  &gt;printf ("Now you see it");&lt;/span&gt;&lt;span style="color: rgb(192, 192, 192);font-size:85%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(192, 192, 192);font-family:courier new;font-size:85%;"  &gt;printf ("Now you don't!");&lt;/span&gt;&lt;span style="color: rgb(192, 192, 192);font-size:85%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(192, 192, 192);font-family:courier new;font-size:85%;"  &gt;*/&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="color: rgb(192, 192, 192);font-family:courier new;font-size:85%;"  &gt;&lt;/span&gt;What happens if the start of the comment block is, itself commented out?&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;&lt;/span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;//*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you see it");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you don't!");&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);font-family:arial;" &gt;*/ syntax error!&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;Not very promising, but let's comment out the end block as well:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;//*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you see it");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you don't!");&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;//*/&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;&lt;/span&gt;&lt;/span&gt;Now, we see the two comment block markers are commented out, and the embedded block is active. It can be toggled on or off simply adding or removing an extra leading slash. So, to deactivate the block with one change:&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-family:arial;"&gt;/*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you see it");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you don't!");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;//*/&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;/span&gt;The benefits of this are that you don't need to hunt through the code looking for the matching end of the comment block.&lt;br /&gt;&lt;br /&gt;Now, we can extend this idea further. Let us split up the block so that one section or the other is not active. The trick here is to identify a character sequence that ends a comment and starts one:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;//*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you see it");&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;/*/&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;printf ("Now you don't!");&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;//*/&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;&lt;/span&gt;&lt;/span&gt;In this case, the first block is active. The new line therefore acts as the start of a comment. However, simply by removing the first slash:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;&lt;/span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;/*&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;printf ("Now you see it");&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;/*/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;printf ("Now you don't!");&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(192, 192, 192);font-family:arial;" &gt;//*/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;The first block is now commented out, and the second block is activated.&lt;br /&gt;&lt;br /&gt;Have fun!&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;super&gt;*&lt;/super&gt;For non-Australians, 'Claytons' is a brand of non-alcoholic beverage popular in the eighties. It was billed as 'the drink you have when you're not having a drink.'. The term 'a clayton's xxx' has passed into common use, with just about anything being substituted (literally!) for 'xxx'.&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-6014087560560778904?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/6014087560560778904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=6014087560560778904' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/6014087560560778904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/6014087560560778904'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/04/claytons-if.html' title='The Clayton&apos;s #if&lt;super&gt;*&lt;/super&gt;'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-6081603583743402121</id><published>2007-03-30T00:09:00.000-07:00</published><updated>2007-03-30T00:44:55.562-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php viewing files problems header ob_start'/><title type='text'>php: When ob_start Is Not Your Friend</title><content type='html'>When programming for the web, you are sooner or later going to start having to look beyond the contents of &amp;lt;body&amp;gt; and messing about with html headers.&lt;br /&gt;&lt;br /&gt;And, in php in particular, you will soon encounter the dreaded error which arises when you call 'header()' after having output some characters.&lt;br /&gt;&lt;br /&gt;Fortunately, this problem is easily fixed by a call to ob_start, which turns on output buffering and stores and collates your html until it's all done. Headers are placed they're meant to be and all is sweetnes and light.&lt;br /&gt;&lt;br /&gt;One common use of headers (which I have just started to get into) is to allow someone to view a file by streaming it through the browser rather than just setting up a url link. (as you might want to do when access to a particular set of files is restricted)&lt;br /&gt;&lt;br /&gt;I'm not going to go into great explanation on how this is done; other and more experienced heads have done this very well already (eg: Leon Atkinson's '&lt;a href="http://www.zend.com/zend/trick/tricks-august-2001.php?article=tricks-august-2001&amp;kind=tr&amp;amp;id=1567&amp;open=1&amp;amp;anc=0&amp;view=1"&gt;Tricks of the Trade&lt;/a&gt;'). I'm more interested in describing a subtle but very annoying gotcha when you start doing this with output buffering in play.&lt;br /&gt;&lt;br /&gt;You see, if you're streaming a binary file (like pdf or jpeg) for viewing, the binary data has to be very precise or the result will be unintelligible garbage. You must be very sure that no other data sneaks into the stream.&lt;br /&gt;&lt;br /&gt;Data such as might be output prior to header calls...&lt;br /&gt;&lt;br /&gt;With ob_start on, you will get no warning of this and, like me, you may spend a good deal of time scratching your head wondering why this stuff doesn't work and probably cursing IE (which is a bit more sensitive to this than is Firefox).&lt;br /&gt;&lt;br /&gt;Without ob_start you will get an illuminating error saying that there already data in the output buffer. In this situation, *any* prior output is poisonous. Not just the result of print and echo statements, but anything that sits outside the &amp;lt;php? and ?&amp;gt; tags. html tags, spaces, carriage returns, anything.&lt;br /&gt;&lt;br /&gt;Fortunately, the error message will usually give you enough information to track down and eliminate the rubbish.&lt;br /&gt;&lt;br /&gt;Spurious output *after* the headers is probably less dangerous, but it's probably just as well to get rid of that as well.&lt;br /&gt;&lt;br /&gt;Hope this helps someone out there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-6081603583743402121?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/6081603583743402121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=6081603583743402121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/6081603583743402121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/6081603583743402121'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/03/php-when-obstart-is-not-your-friend.html' title='php: When ob_start Is Not Your Friend'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-5743060161288592148</id><published>2007-03-27T21:18:00.000-07:00</published><updated>2007-03-27T23:51:12.335-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='object independence'/><title type='text'>The Links That Bind</title><content type='html'>My daughter is usually driven to and from school during the week by her nanny. For this reason, the   child seat usually stays in the nanny's car until the weekend.&lt;br /&gt;&lt;br /&gt;This has been fine up until recently, when the nanny's car started going through a rough patch mechanically. Twice, in the last fortnight, I have received a call that the car has broken down, or won't start. Apart from the inconvenience of taking 'little Missy' to school ourselves before going to work, we have had to retrieve the means of getting her to school: ie the child seat! This, at a time when an already overloaded traffic system was thrown into chaos by the domain tunnel closure, made for even more time lost.&lt;br /&gt;&lt;br /&gt;We've actually been pretty lucky wrt absences. If the nanny has called in sick, it's usually been at the start or end of the week, and we have had the seat. Not this time!&lt;br /&gt;&lt;br /&gt;The simple interim solution has been to agree that the seat is to be left at our house in the evenings.&lt;br /&gt;&lt;br /&gt;So, you might be wondering what this little domestic drama has to do with bad programming? Simply to point out that, while it might be convenient to have underlying links between class  objects that have a lot to do with each other, greater flexibility is achieved when you define the links explicitly with each transaction.&lt;br /&gt;&lt;br /&gt;Just as we now put in the child seat each morning and take it out again in the evening, always think about passing a reference to another class as a parameter and don't expect it to be stored, or picked it up from somewhere. The onus on providing the necessary information for an object&lt;sup&gt;*&lt;/sup&gt; to do its job should rest with the client program. That way, your objects can exist a little more independently of each other.&lt;br /&gt;&lt;br /&gt;And so can you!&lt;br /&gt;&lt;sup&gt;&lt;/sup&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;sup&gt;*&lt;/sup&gt;For the record, I do not consider my daughter's nanny as an object!&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-5743060161288592148?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/5743060161288592148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=5743060161288592148' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/5743060161288592148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/5743060161288592148'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/03/links-that-bind.html' title='The Links That Bind'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-4899940534803810045</id><published>2007-03-19T05:05:00.000-07:00</published><updated>2007-03-19T05:30:14.011-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript checkbox delayed action explorer'/><title type='text'>Ons Ain't Ons</title><content type='html'>An interesting little javascript gotcha bit me just recently.&lt;br /&gt;&lt;br /&gt;... only I'm not sure whether it was javascript, or Firefox, or (oh, surely not!?) Internet Explorer.&lt;br /&gt;&lt;br /&gt;To begin at the beginning, I had a fairly straightforward piece of javascript code working quite nicely in Firefox. Then I tried it in IE, and encountered a truly bizarre disconnect (literally)&lt;br /&gt;&lt;br /&gt;What it boiled down to was that the js script was delayed in  execution after clicking a checkbox: You'd click on the checkbox and nothing would happen. Then you'd click on something else, and the script would wake up and hurry to the bus stop.&lt;br /&gt;&lt;br /&gt;After a bit of poking around, I found the problem: for reasons that escape me now (probably taking the lead from another script meant for use in a combo box), I was using an 'onModify' event to trigger my script, rather than 'onClick'. The problem this was cauing me arises in the way that different browsers handle these events.&lt;br /&gt;&lt;br /&gt;Firefox thinks you change the content of a checkbox when you click on it, so it runs the script immediately.&lt;br /&gt;&lt;br /&gt;Explorer, on the other hand, decides that changes to data should be acted on when the control has lost focus (and no one's looking), so it waits.&lt;br /&gt;&lt;br /&gt;Solution: use 'onClick' whenever you wish to react to changed content of a checkbox.&lt;br /&gt;&lt;br /&gt;One is inclined to give in to the temptation to knock IE6 because it's the done thing but, to be honest, I couldn't call the IE behaviour explicitly wrong and I don't know what, if anything,  the standards say about when event handling should occur.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-4899940534803810045?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/4899940534803810045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=4899940534803810045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/4899940534803810045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/4899940534803810045'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/03/ons-aint-ons.html' title='Ons Ain&apos;t Ons'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4485741147814538981.post-5709716065119786266</id><published>2007-03-13T17:00:00.000-07:00</published><updated>2007-03-13T17:17:34.156-07:00</updated><title type='text'>What's This About?</title><content type='html'>&lt;blockquote style="font-style: italic;"&gt;&lt;span style="font-size:78%;"&gt;"To err is you, man!"&lt;/span&gt;&lt;/blockquote&gt;As part of my general online presence, I very occasionally print a couple of coding suggestions.&lt;br /&gt;&lt;br /&gt;Prompted, in part, by Tim Bray's brief note about '&lt;a href="http://www.tbray.org/ongoing/When/200x/2007/03/10/Beautiful-Code"&gt;Beautiful Coding&lt;/a&gt;', I thought I'd start gathering these together in one spot.&lt;br /&gt;&lt;br /&gt;They won't be anything to set the world abuzz, but there have been times when I've trawled the web looking for how to handle that 'obvious' situation, only to find my brain seems to operate in widely divergent ways to everyone else's.&lt;br /&gt;&lt;br /&gt;Of course, this is an affectation: I'm sure there have been others that have asked some of the questions I have. So this is an attempt to answer them.&lt;br /&gt;&lt;br /&gt;Now, I'm not sure whether it's better to do this as a blog, or as a wiki. At the time of writing, my thinking is obviously inclined to the former. Plus, it allows the passing traffic to offer the  occasional friendly addendum and raspberry (..NB: I did say &lt;span style="font-style: italic;"&gt;friendly&lt;/span&gt;!)&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;&lt;span style="font-size:78%;"&gt;"Learn from other people's mistakes. It's less embarrassing that way."&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4485741147814538981-5709716065119786266?l=badcoding.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://badcoding.blogspot.com/feeds/5709716065119786266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4485741147814538981&amp;postID=5709716065119786266' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/5709716065119786266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4485741147814538981/posts/default/5709716065119786266'/><link rel='alternate' type='text/html' href='http://badcoding.blogspot.com/2007/03/whats-this-about.html' title='What&apos;s This About?'/><author><name>Tony Fisk</name><uri>http://www.blogger.com/profile/14578160528746657971</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/-0zHQlvbU1Sk/Trm-6rIX7WI/AAAAAAAAAJ4/oC8BygCoX2o/s1600/WikiTikiTaviInverse_bigger.jpg'/></author><thr:total>0</thr:total></entry></feed>
