<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Inversionism | Paul Hammant's blog</title><link>http://paulhammant.com/</link><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/paulhammant" /><language>en</language><lastBuildDate>Thu, 26 Apr 2012 21:21:46 PDT</lastBuildDate><feedburner:info uri="paulhammant" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><description></description><item><title>Call to Arms: Average Story Sizes of One Day</title><link>http://feedproxy.google.com/~r/paulhammant/~3/3PxmD-bLPs8/</link><pubDate>Tue, 24 Apr 2012 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/04/24/call-to-arms-average-story-sizes-of-one-day</guid><description>&lt;p&gt;Synopsis: Story sizes should average as close to one day as possible&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;. If they don&amp;#8217;t your Agile project is going to be harder for nearly everyone involved. If your average is significantly greater than that one day, then change something until you get there. Change technologies if you have to, or split stories, or eliminate NFRs (or delay them). Of course, I assume you know what &amp;#8216;Agile&amp;#8217; and &amp;#8216;Stories&amp;#8217; are. *So, I&amp;#8217;m hereby calling on PMs and BAs to do their part to developers live in this zone (details below).&lt;/p&gt;
&lt;p&gt;A week ago, I talked about the &lt;a href="http://paulhammant.com/2012/04/15/application-development-glass-ceilings"&gt;Glass Ceilings in Application Development&lt;/a&gt; and that&amp;#8217;s generally applicable to this topic too. But this blog entry is concerned with the costs of having longer estimates. Developers cannot estimate longer stories accurately. It is just impossible to consistently go above that and be accurate.&lt;/p&gt;
&lt;p&gt;So what was intended for Agile/XP when all this stuff was new (and projects were smaller)? I suggest (synopsis) that the XP pioneers said &lt;strong&gt;&amp;#8220;we can&amp;#8217;t tell you how many iterations it is going to take, or what the final cost will be, but we can commit to shipping software in iterations, concentrating on business value, and giving you moments where you can choose to complete, pause, or abandon development of the product/service&amp;#8221;&lt;/strong&gt;, and to that end, they envisaged smaller than 3-day stories.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/cards.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;What to do if you have longer averages?&lt;/h2&gt;
&lt;p&gt;What if you average is significantly longer than that one-day mark?  What if it is, say, 3 days with the increased risk to measured progress and team morale?  Work out what is wrong, and change it, is the answer.&lt;/p&gt;
&lt;h3&gt;Wrong People?&lt;/h3&gt;
&lt;p&gt;It could be that you could change the developers for the project. Developers are the biggest percentage of resource costs when considering the price of a story. Say you have a project that uses Hibernate, you may get a boost from hiring Gavin King (who is/was the Lead Developer for Hibernate). He may not thank you for plucking him from his yacht, but he really knows that stuff. How much can be gained from having experts on those technologies which are just part of a larger stack?  How much faster are the experts versus good generalists within the team?  It seems that perhaps only modest gains could be made, and as you implicitly lose the generalists, you lose the blended-team productivity benefits.&lt;/p&gt;
&lt;h3&gt;Wrong Technologies?&lt;/h3&gt;
&lt;p&gt;Maybe the technologies are wrong. Following on from that topic from a week ago, I suggesting that we can, unfortunately, choose technologies that really make it hard for us to complete what were trying to do. Here&amp;#8217;s an example: Hardy Leung has made the excellent Tagxedo:http://www.tagxedo.com/ site. It is mostly &lt;span class="caps"&gt;HTML&lt;/span&gt; of course, but a critical design tool for word clouds is written using Microsoft&amp;#8217;s Silverlight technology. Hardy might have started with a &lt;span class="caps"&gt;HTML&lt;/span&gt; site and &lt;strong&gt;quickly&lt;/strong&gt; imagined an eternity of effort to get his vision complete. He settled on Silverlight for the best &amp;#8216;bang for the buck&amp;#8217;, and having used the app on my Mac, I can say that he has done a great job. Near the bottom of his &lt;a href="http://www.tagxedo.com/faq.html"&gt;&lt;span class="caps"&gt;FAQ&lt;/span&gt;&lt;/a&gt; he goes into his rationale for choosing Silverlight. That is one of a few &amp;#8216;right&amp;#8217; technologies for this job, as of Q2 2012.&lt;/p&gt;
&lt;p&gt;Changing technologies is not cheap of course. Sometimes you&amp;#8217;ve gone too far to change, and it&amp;#8217;s better to soldier on. Quickly identifying the need to change is best. Perhaps best of all during the &amp;#8216;experimenting with technolgies&amp;#8217; phase before ramping up the project. Again, be careful not to choose one with glass ceilings.&lt;/p&gt;
&lt;h4&gt;Rule of thumb&lt;/h4&gt;
&lt;p&gt;For the technologists amongst us, critically estimate the number of lines executed (non-framework, non-library) from the &amp;#8216;click&amp;#8217; the user did all the way down the stack and back up again for the response that changes the UI.  That counts JavaScript, Java, Ruby or C#, and stops at &lt;span class="caps"&gt;SQL&lt;/span&gt;.  If you&amp;#8217;ve busted the facade pattern, then count all those tendrils down the stack. That is, unless your client caching strategy can massively mitigate&lt;sup class="footnote"&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt; that. Aim at a max of 100 lines executed.  If you&amp;#8217;re not hitting that, ask for simpler technologies in order to be able to hit the one day story average.&lt;/p&gt;
&lt;h3&gt;Story Analysis wrong?&lt;/h3&gt;
&lt;p&gt;Perhaps the stories do not adhere to the &lt;a href="http://en.wikipedia.org/wiki/Extreme_programming"&gt;Extreme Programming&lt;/a&gt; ideals of &lt;a href="http://en.wikipedia.org/wiki/Vertical_slice"&gt;thin vertical slice&lt;/a&gt; or the &lt;a href="http://en.wikipedia.org/wiki/INVEST_(mnemonic)"&gt;&lt;span class="caps"&gt;INVEST&lt;/span&gt;&lt;/a&gt; principle. That spelled out is &amp;#8220;Independent, Negotiable, Valuable, Estimable, Small and Testable&amp;#8221;. Perhaps before estimation more representatives of the development team can advise on where to make splits in stories, and the Business Analysts still have time to rework them with the customer&amp;#8217;s agreement before that estimation session. These should be outliers though. Story splitting I mean. A team that is zipping along will be feeding developers stories where the sizing is optimal, and if they are too large (from somebody&amp;#8217;s perception) they can&amp;#8217;t be split any further, and something else is &amp;#8216;wrong&amp;#8217;. I&amp;#8217;d never like BAs to just accept the large-story scenario though; I&amp;#8217;d implore them to respectfully push back at the project manager, stakeholders and architects in the team to try to change something so that the one-day average could become true. &lt;strong&gt;If that means they have to say &amp;#8220;it looks like our technologies are too complicated&amp;#8221; ad they see it, then so be it&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;NFRs killing you?&lt;/h3&gt;
&lt;p&gt;Non-functional Requirements; Hmmm. The cost of implementing something in a technology is one thing, but Agile teams have an additional factor to worry about: They frequently revisit code that is complete to a certain level, and move it incrementally as new stories come in. That can include refactoring. So is the technology or the way you are using it friendly to refactoring?  Quite often the NFRs for an Agile team mandate the least-refactorable technologies in to the mix.&lt;/p&gt;
&lt;h4&gt;&lt;span class="caps"&gt;CORBA&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span class="caps"&gt;CORBA&lt;/span&gt; is traditionally a technology that&amp;#8217;s not easy to refactor in the best of breed IDEs. It is bound to be an &lt;span class="caps"&gt;NFR&lt;/span&gt;, as its not often going to be chosen given free choice.&lt;/p&gt;
&lt;p&gt;Say you have a three-tier architecture, and the architects stipulated &lt;span class="caps"&gt;CORBA&lt;/span&gt; as the &lt;span class="caps"&gt;RPC&lt;/span&gt; linkage between a service tier and its client in the same language. You might have a future requirement to bolt on a second client in a different language, making &lt;span class="caps"&gt;CORBA&lt;/span&gt; a common choice for later on in the project. At the beginning you had many technology choices. Maybe the language in question had something more canonical for it (like Ruby&amp;#8217;s &lt;a href="http://segment7.net/projects/ruby/drb/"&gt;DRb&lt;/a&gt;). Using that might be a lot easier than crafting all the &lt;span class="caps"&gt;IDL&lt;/span&gt; that &lt;span class="caps"&gt;CORBA&lt;/span&gt; deems necessary. Perhaps you should leverage an open source library that makes for RESTful endpoints into the service tier, that is again easier to use, but with the added benefit of maybe being good enough for the future second client.  Even if you &amp;#8216;must have&amp;#8217; &lt;span class="caps"&gt;CORBA&lt;/span&gt; in that final multi-client technology version, you could have gained initial functional productivity using easier technologies and added a small &lt;span class="caps"&gt;CORBA&lt;/span&gt; layer later. I.e. delay is a good choice, if you plan carefully. There are other &lt;span class="caps"&gt;RPC&lt;/span&gt; choices that are multi-language that are also viable, and perhaps easier to deal with that &lt;span class="caps"&gt;CORBA&lt;/span&gt;: &lt;a href="http://thrift.apache.org/"&gt;Thrift&lt;/a&gt; and &lt;a href="http://www.flotype.com/"&gt;FlowType Bridge&lt;/a&gt; are just two.&lt;/p&gt;
&lt;h4&gt;Persisting to a normalized database&lt;/h4&gt;
&lt;p&gt;Maybe its a requirement for a relation schema that&amp;#8217;s been normalized to the nth degree &lt;strong&gt;and&lt;/strong&gt; having referential integrity turned in with constraints &lt;strong&gt;and&lt;/strong&gt; triggers. If you &amp;#8216;only&amp;#8217; have a UI interpreting with that store, and that UI needs a &lt;span class="caps"&gt;JSON&lt;/span&gt; document to and from into today&amp;#8217;s cheapest web technologies (AngularJS or Knockout) then why bother with the relational schema?  Use a document store for the time being, and migrate to a relational schema later in the project. Read up on &lt;a href="http://en.wikipedia.org/wiki/NoSQL"&gt;NoSQL&lt;/a&gt; people.&lt;/p&gt;
&lt;p&gt;You might do a document-store solution for pure productivity reasons, but have a known relation schema need later in the project for operation or managerial reports. You still have choices. You could create a extract process that takes the written document data and writes it to relational tables to be used in reports. That could happen in real time, or in a daily batch extract.&lt;/p&gt;
&lt;h4&gt;Multi-tier per se.&lt;/h4&gt;
&lt;p&gt;Is it possible that you&amp;#8217;ve got the tiers that the architecture team want for the final product, and there&amp;#8217;s a lot of fairly predictable transformation logic going on up and down the stack to separate the models. It could be that you could gain productivity by delaying a tier separation. Perhaps &lt;a href="http://en.wikipedia.org/wiki/Conway&amp;#39;s_law"&gt;Conway&amp;#8217;s Law&lt;/a&gt; &lt;sup class="footnote"&gt;&lt;a href="#fn3"&gt;3&lt;/a&gt;&lt;/sup&gt; workshops should be and yearly and mandatory for managers, project check-book holders, executives and architects with IT organisations :)&lt;/p&gt;
&lt;h2&gt;Technical-debt stories.&lt;/h2&gt;
&lt;p&gt;Obviously by delaying work we may end up with a number of &lt;a href="http://en.wikipedia.org/wiki/Technical_debt"&gt;technical debt&lt;/a&gt; stories. They are controversial, as the customer may not ultimately map them to features they have asked for. In later phases of the story, the customer may be used to the horse-trading stage of iteration planning and killing stories that have no business value.  Finding the right way of describing what is being paid for later is key. I&amp;#8217;ve seen such stories explain well in terms of &lt;a href="http://cdn.pols.co.uk/papers/cutterbusinessvaluearticle.pdf"&gt;business value&lt;/a&gt;. Your risk at this moment is that a huge piece of work has been delayed, and someone want to do this on a branch over a month, rather than &lt;a href="http://paulhammant.com/blog/branch_by_abstraction.html"&gt;Branch by Abstraction&lt;/a&gt;. Maybe as you decide to delay non-functional work, you&lt;/p&gt;
&lt;h2&gt;Lastly.&lt;/h2&gt;
&lt;p&gt;Colleague Derek Hammer reminds me of one the fundamental benefits of this proposal: Consistent throughput/liquidity because &amp;#8216;small&amp;#8217; stories are the average.  There are some stats involved, but the gist is that we have less waits on stories being completed. We also have less &amp;#8216;hangover&amp;#8217; of nearly completed stories, the points/days for which &amp;#8216;banked&amp;#8217; in the next iteration.  Here that problem in a chart form:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/iteration_points.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;How is the project doing?  What is the trend for story points per iteration?  I don&amp;#8217;t think we can&amp;#8217;t really tell from that chart.&lt;/p&gt;
&lt;h2&gt;Footnotes.&lt;/h2&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; - Stories have estimates and actual times. I&amp;#8217;m assuming here that your team has these more or less the same as you get into your stride.&lt;/p&gt;
&lt;p class="footnote" id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; - Please read Steve&amp;#8217;s Souder&amp;#8217;s &lt;a href="http://www.stevesouders.com/blog/2012/03/22/cache-them-if-you-can"&gt;Cache them if you can&lt;/a&gt; blog entry.&lt;/p&gt;
&lt;p class="footnote" id="fn3"&gt;&lt;sup&gt;3&lt;/sup&gt; - Thanks to colleague James Lewis for the reminder.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/3PxmD-bLPs8" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/04/24/call-to-arms-average-story-sizes-of-one-day/</feedburner:origLink></item><item><title>Application Development Glass Ceilings</title><link>http://feedproxy.google.com/~r/paulhammant/~3/IqoffYcpAKs/</link><pubDate>Sun, 15 Apr 2012 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/04/15/application-development-glass-ceilings</guid><description>&lt;p&gt;Sometimes in app-dev we&amp;#8217;re using technologies that have a delayed cost. Simple to use now, but with shortcomings that mean that later features might be much harder to add. The term &amp;#8216;Glass Ceilings&amp;#8217; (&lt;a href="http://en.wikipedia.org/wiki/Glass_ceiling"&gt;whilst classically meaning something else&lt;/a&gt;) seems to apply here.&lt;/p&gt;
&lt;h2&gt;An Example&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/combobox_glass_ceiling.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll Assume you are familiar with Agile. The customer/product-owner asks for a change to an existing dropdown in the UI. She wants a picture of the fruit next to the name of the fruit. He remembers Visual-Basic application development from the mid 90&amp;#8217;s where this was easy. Also, She&amp;#8217;s seen it in Java-Swing applications not so long ago. Now, that most enterprise apps are web-based, and fifteen years have passed since She first saw it, She thinks it should be easy!&lt;/p&gt;
&lt;p&gt;Well, it is not. &lt;span class="caps"&gt;HTML&lt;/span&gt; does not support this be default. Most formal Web-Frameworks don&amp;#8217;t do icon+text combo boxes. If they do it at all, it is because the authors have written something special in JavaScript. It&amp;#8217;s a very custom control in &lt;span class="caps"&gt;HTML&lt;/span&gt;-land.&lt;/p&gt;
&lt;p&gt;Lets say that the original feature (dropdown of fruit names load and saving to the database for the &amp;#8216;thing&amp;#8217; in question) took a day to write. If we&amp;#8217;re using a technology that does not have that feature we are going to have to go back to the product owner with some bad news or &lt;span class="caps"&gt;VERY&lt;/span&gt; bad news. It could take an eternity or be impossible to do it with the technology we are currently using.&lt;/p&gt;
&lt;p&gt;What you really hope for is an &amp;#8216;easy&amp;#8217; addition of something that&amp;#8217;s correct for your technology choices. If you were using JQuery as your substantial in-browser technology, or were compatible with it, and you had this exact problem, then you&amp;#8217;d be very happy that you found Fairway Technology&amp;#8217;s FlexBox. &lt;a href="http://www.fairwaytech.com/flexbox/flexbox-demos/#demo7"&gt;Refer example 7 on their demo site&lt;/a&gt; like so:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/fairway_tech_flexbox.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;Punching through the glass ceiling yourself&lt;/h2&gt;
&lt;p&gt;If we are using a commercial closed-source technology we are beholden to the vendor to even think our change request is important, or have a channel open for such communication. Moreover, we hope they are a competent outfit, and can ship a release quickly. If the technology is open source, then we&amp;#8217;ve recourse to fork it and make a change ourselves. In that eventuality, we hope that the canonical version&amp;#8217;s team will accept back the changes, and not make too many changes. Incidentally, this is one place that open source teams fails &amp;#8211; &lt;strong&gt;not&lt;/strong&gt;-facilitating someone else&amp;#8217;s idea for that technology.&lt;/p&gt;
&lt;p&gt;The worst-case scenario is that you have to change technologies. You might be halfway through the product buildout cycle, and heads will roll on this bad news!&lt;/p&gt;
&lt;p&gt;The best-case scenario, is that the open or closed source team has &amp;#8216;plugin&amp;#8217; mechanism for adding capability. Even better if there&amp;#8217;s a way of leveraging/extending supplied components.&lt;/p&gt;
&lt;p&gt;Even in the best-case scenario we&amp;#8217;re going to have to suggest to the product owner that the feature She needs is going to take five days, but after that&amp;#8217;s done, similar changes to other dropdowns will take half a day.&lt;/p&gt;
&lt;p&gt;Thus, in summary, we hope that if we encounter glass ceilings during the building of an app, we can punch though them. Perhaps we are unsure about how much effort that will be in advance of encountering it. It is probably best that we are diligent in technology choices up front. I&amp;#8217;m going to return to that particular thought in my next blog entry.&lt;/p&gt;
&lt;h2&gt;Not just UI controls&lt;/h2&gt;
&lt;p&gt;The problem of glass ceilings does not just apply to UI affordances and effects. You could encounter it in mismatches between a self-designed relation-schema for your app, and perhaps the Object-Relational Mapping (&lt;span class="caps"&gt;ORM&lt;/span&gt;) technology you chose. Perhaps your synchronous &lt;span class="caps"&gt;RPC&lt;/span&gt; technology should have been asynchronous.&lt;/p&gt;
&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve talked about this before &lt;a href="http://paulhammant.com/blog/ruby-versus-javascript-for-web3.0.html"&gt;Ruby versus JavaScript for Web3.0&lt;/a&gt; in 2006. That one was proposing changes to web-app coding that have not come into fruition. Perhaps they could with a future version of Dart, but I&amp;#8217;m not chasing that agenda for the time being.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll link to other proper &lt;span class="caps"&gt;HTML&lt;/span&gt; combobox solutions if people email me, or comment below.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/IqoffYcpAKs" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/04/15/application-development-glass-ceilings/</feedburner:origLink></item><item><title>Performance Testing Knockout, Angular and Backbone with Selenium2</title><link>http://feedproxy.google.com/~r/paulhammant/~3/VML6dLB_I0c/</link><pubDate>Thu, 12 Apr 2012 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/04/12/performance-testing-knockout-angular-and-backbone-with-selenium2</guid><description>&lt;p&gt;Following on from &lt;a href="http://paulhammant.com/2012/04/09/testing-knockout-and-angular-with-selenium2/"&gt;Monday&amp;#8217;s Selenium2 demo of &amp;#8216;functional testing demo&amp;#8217; for Angular and Knockout&lt;/a&gt;, I thought I&amp;#8217;d expand what I had into a performance test seeing as there are no server GETs or POSTs after loading each &lt;span class="caps"&gt;TODO&lt;/span&gt;-&lt;span class="caps"&gt;MVC&lt;/span&gt; app. Monday&amp;#8217;s test scripts have been morphed into ones that are less a demonstration of fluent-selenium, and more a harness for the repeated addition of TODOs into the &lt;span class="caps"&gt;TODO&lt;/span&gt;-list. Specifically, rather than loop through items, I&amp;#8217;m going straight to the last matching one for an XPath selector via &amp;#8216;//whatever[xxxxx and position() = last()]&amp;#8217;. I have a &lt;a href="https://github.com/paul-hammant/groovy_todomvc_performance_scripts"&gt;github project for the test scripts and the results&lt;/a&gt; (&lt;span class="caps"&gt;CSV&lt;/span&gt;). Selenium2 has a &amp;#8216;feature&amp;#8217; whereby it needs to have the widget that it&amp;#8217;s intersecting with visible on the page, and consequentially does a lot of scrolling for these apps. I want to add 1000 TODOs for this performance test, and fear the cost of the constant scrolling within the browser. I measured it, and the cost is real &amp;#8211; like up to 10% of the over all time taken for the test, so I&amp;#8217;ve forked Addy Osmani&amp;#8217;s &lt;span class="caps"&gt;TODO&lt;/span&gt;-&lt;span class="caps"&gt;MVC&lt;/span&gt; demo apps to make the input field for &amp;#8220;new TODOs&amp;#8221; at the bottom of the list rather than the top. I&amp;#8217;ve also deliberately detached the &lt;span class="caps"&gt;CSS&lt;/span&gt;, so that thus is a raw performance test of the Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt; framework. I could not measure much of a difference for that last though. The test comprises i) Adding a &lt;span class="caps"&gt;TODO&lt;/span&gt;, ii) Locating it in the list and verifying that it was added, and iii) clicking the &amp;#8220;done&amp;#8221; checkbox. That means that the substantial testing of the Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt; framework was an a) add to the model, b) a change to an item in the model, c) view updates to reflect that, and d) a consequential change to a &amp;#8216;remaining todos&amp;#8217; counter. I&amp;#8217;ve also tested that Selenium2 itself is not responsible for the slow-down by timing some of its &lt;span class="caps"&gt;DOM&lt;/span&gt; traversals that are not associated with a change to the model.&lt;/p&gt;
&lt;h2&gt;Results.&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_mvc_perf.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Can you see that?  Backbone and Knockout are kinda constant at 132ms and 123ms respectively per iteration. Angular starts slight better than those two, but moves up to 400ms per iteration by #1000. Click &lt;a href="http://paulhammant.com/images/todo_mvc_perf_big.pdf"&gt;here to see a large &lt;span class="caps"&gt;PDF&lt;/span&gt; of the whole chart&lt;/a&gt; (sideways scrolling required).&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve done multiple runs on this 8Gb &lt;span class="caps"&gt;SSD&lt;/span&gt; Mac to ensure consistency. The AngularJS chart, for one, was odd enough to warrant further inspection. Here are two runs overlaid on the same chart (and zoomed in):&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_mvc_angular_second_run.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Weird huh? Almost identical. I can only think that&amp;#8217;s an interaction between AngularJS, the JavaScript environment of Firefox 10.0.2, and the vanilla JavaScript model. It could be that Selenium2 is erroneously waiting for interactions to finish slightly longer than necessary, meaning the slow-down is not real. Specifically, AngularJS polling for changes in either direction, might be enough to thwart Selenium&amp;#8217;s &amp;#8220;document ready&amp;#8221; detection.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I still think that the productivity benefits of AngularJS makes it very much &amp;#8216;worth it&amp;#8217; compared to Backbone. AngularJS at the 2001 model-bound nodes in the view (1000 output fields from a model + 1000 input/output rows from that model + 1 calculated field) is still working faster than humans can type. KnockoutJS takes slightly more coding that the equivalent AngularJS app, but has the best performance at that 2001 node limit. Backbone takes much more coding, making your productivity benefits dissapear. I&amp;#8217;d still use Knockout and AngularJS instead of Backbone every time.&lt;/p&gt;
&lt;h2&gt;JQuery Foot-note.&lt;/h2&gt;
&lt;p&gt;I was going to similarly measure the performance of Addy&amp;#8217;s &lt;a href="http://addyosmani.github.com/todomvc/architecture-examples/jquery/"&gt;JQuery+Moustache&lt;/a&gt; example, but Selenium could not locate the input-field when I moved it down the page. If I left it at the top, and ran the test anyway, it was taking 600ms per iteration at the end. Here&amp;#8217;s a video that has &lt;span class="caps"&gt;NOT&lt;/span&gt; been slowed down:&lt;/p&gt;
&lt;p&gt;&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/8uy_lWTLiSo" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;PS &amp;#8211; thanks to colleague Tarek Abdelmaguid for the backbone version of the groovy script.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/VML6dLB_I0c" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/04/12/performance-testing-knockout-angular-and-backbone-with-selenium2/</feedburner:origLink></item><item><title>Testing Knockout and Angular with Selenium2</title><link>http://feedproxy.google.com/~r/paulhammant/~3/dY5RIxdNk1g/</link><pubDate>Mon, 09 Apr 2012 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/04/09/testing-knockout-and-angular-with-selenium2</guid><description>&lt;p&gt;Selenium2 (&lt;span class="caps"&gt;AKA&lt;/span&gt; &amp;#8212;WebDriver) is the de-facto standard web testing technology.  It is good that Selenium &lt;a href="http://www.indeed.com/jobtrends/qtp,selenium.html"&gt;now garners more job postings than &lt;span class="caps"&gt;QTP&lt;/span&gt;&lt;/a&gt;, but that&amp;#8217;s a different topic.  the Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt; frameworks are a boon to web app development, but what about writing test scripts for the finished product?  In this blog entry I will show how testable &lt;a href="http://angularjs.org"&gt;AngularJS&lt;/a&gt; and &lt;a href="http://knockoutjs.com/"&gt;KnockoutJS&lt;/a&gt; applications are.  In my opinion these two are the leading client-side &lt;span class="caps"&gt;MVC&lt;/span&gt; technologies, though there are alternatives like &lt;a href="http://documentcloud.github.com/backbone"&gt;Backbone.js&lt;/a&gt; that have significant market share. I prefer the two I&amp;#8217;m highlighting over Backbone for a range of reasons.&lt;/p&gt;
&lt;h2&gt;Testing a KnockoutJS app with Selenium2&lt;/h2&gt;
&lt;p&gt;There&amp;#8217;s a &lt;span class="caps"&gt;TODO&lt;/span&gt; app on Addy Osmani&amp;#8217;s &lt;a href="http://addyosmani.github.com/todomvc"&gt;technology comparison site&lt;/a&gt;.  It uses Knockout 2.0 (Knockout 2.1 is in beta-testing presently).  Here&amp;#8217;s what it looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/knockout_todo.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;In testing the app, I&amp;#8217;m going to try to write for an idiomatic Knockout app.  Here&amp;#8217;s the &lt;span class="caps"&gt;DOM&lt;/span&gt; as Firebug sees it:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/knockout_todo_dom.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve highlighted &amp;#8216;1&amp;#8217; &amp;#8211; the input line where new entries are posted, and &amp;#8216;2&amp;#8217; an attribute where Knockout notes that it is looping through todo items.  Prevalent througout the &lt;span class="caps"&gt;DOM&lt;/span&gt; are many more &amp;#8216;data-bind&amp;#8217; attributes. These are evidence for Knockout managing the page.  &lt;span class="caps"&gt;HTML&lt;/span&gt; does not have that attribute, only Knockout understands it. The browser just ignores the attributes it does not understand.&lt;/p&gt;
&lt;p&gt;Here is the Selenium2 script (Groovy) that tests this app.  It is going to add and item, then mark it as &amp;#8216;done&amp;#8217;, then confirm that the UI displays it as done with a strike-through:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@Grapes&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium:selenium-java:2.20.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium.fluent:fluent-selenium:1.5.1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; 
 &lt;span class="nd"&gt;@GrabExclude&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;apis:&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apis&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;])&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.By&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.firefox.FirefoxDriver&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.Keys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.StaleElementReferenceException&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.seleniumhq.selenium.fluent.FluentWebDriverImpl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hamcrest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CoreMatchers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;junit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seleniumhq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FluentBy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attribute&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seleniumhq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;secs&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FirefoxDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;fluent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FluentWebDriverImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://addyosmani.github.com/todomvc/architecture-examples/knockoutjs/index.html&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;abc 123 def 456&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// there are many angular examples in one page&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;within&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todoapp&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// add an entry&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;contains(@data-bind, &amp;#39;enterKey: add&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RETURN&lt;/span&gt;

&lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;no items checked initially&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;done&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;lis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;data-bind&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;foreach: todos&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// loop through the todo items ..&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// .. click the checkbox for the matching TODO item&lt;/span&gt;
    &lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
	&lt;span class="k"&gt;break&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;one item checked&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;done&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Test Duration: &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot; milliseconds.&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The best elapsed time for the test was 326 milliseconds.  Certainly faster than a human could type.&lt;/p&gt;
&lt;h2&gt;Testing a AngularJS app with Selenium2&lt;/h2&gt;
&lt;p&gt;Angular is gearing up for a 1.0 release presently.  That&amp;#8217;s about time after 2.5 years!  As such, I&amp;#8217;ve copied their current &lt;span class="caps"&gt;TODO&lt;/span&gt; example to &lt;a href="http://paul-hammant.github.com/angular_todo_app"&gt;a stand-alone GitHub-Pages site&lt;/a&gt;.  Here is what that app looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/angular_todo.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the &lt;span class="caps"&gt;DOM&lt;/span&gt; as seen in Firebug.  Again the input field and looping construct are shown:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/angular_todo_dom.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Angular uses more fine-grained attributes than Knockout. Again, attributes that only it understands. ng-repeat, ng-model, ng-click, ng-show are just some of them.  This might make it slightly easier to leverage them for Selenium2 locators.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@Grapes&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium:selenium-java:2.20.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium.fluent:fluent-selenium:1.5.1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; 
 &lt;span class="nd"&gt;@GrabExclude&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;apis:&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apis&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;])&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.By&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.firefox.FirefoxDriver&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.Keys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.StaleElementReferenceException&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.seleniumhq.selenium.fluent.FluentWebDriverImpl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hamcrest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CoreMatchers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;junit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seleniumhq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FluentBy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attribute&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seleniumhq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;secs&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FirefoxDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;fluent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FluentWebDriverImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://paul-hammant.github.com/angular_todo_app/&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;abc 123 def 456&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ng-controller&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;TodoCtrl&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// add an entry&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ng-model&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;todoText&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RETURN&lt;/span&gt;
&lt;span class="c1"&gt;//form.input(attribute(&amp;quot;type&amp;quot;, &amp;quot;submit&amp;quot;)).click()&lt;/span&gt;

&lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;one item checked&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;spans&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;done-true&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;lis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ng-repeat&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;todo in todos&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// loop through the todo items ..&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// .. click the checkbox for the matching TODO item&lt;/span&gt;
    &lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
	&lt;span class="k"&gt;break&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;two items checked&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;spans&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;done-true&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Test Duration: &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot; milliseconds.&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The best time for the Angular/Groovy test was 330 milliseconds which is more or less the same as the Knockout one.  Still faster than humans can type.  It is also important to note that the Angular team have changed from &amp;#8216;ng&amp;#8217; namespace found in the pre-1.0 releases that meant that colons were prevalent in the &lt;span class="caps"&gt;HTML&lt;/span&gt; soure, to a design where a dash is used instead.  What was ng:show is now ng-show (etc).  This is great news for Selenium2 testing as the colons were a bit of a problem. I &lt;a href="http://paulhammant.com/2012/03/15/angular-and-selenium-again"&gt;blogged previously&lt;/a&gt; on that.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Angular and Knockout provide some handy hooks for Selenium2 to navigate by.  Though IDs managed by the framework would be better, what&amp;#8217;s there is good enough.  Angular using more fine-grained attributes rather than the little language that Knockout has in a single attribute and that is going to be easier to navigate with.  That said convenience locators could be made for either:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="c1"&gt;// Angular&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todoText&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RETURN&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;lis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;repeat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo in todos&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// Knockout&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ko&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enterKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;add&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RETURN&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;lis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ko&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foreach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todos&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;lis&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/dY5RIxdNk1g" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/04/09/testing-knockout-and-angular-with-selenium2/</feedburner:origLink></item><item><title>AngularJS and Selenium (Again)</title><link>http://feedproxy.google.com/~r/paulhammant/~3/CUAekP96Tso/</link><pubDate>Thu, 15 Mar 2012 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/03/15/angular-and-selenium-again</guid><description>&lt;p&gt;Update (Apr 9, 2012):  A new version of Angular is much easier to test with Selenium2 now.  Read about that in the follow up blog entry: &lt;a href="http://paulhammant.com/2012/04/09/testing-knockout-and-angular-with-selenium2"&gt;Testing Knockout and Angular with Selenium2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Synopsis:  Using Selenium with Angular&amp;#8217;s not so easy right now.&lt;/p&gt;
&lt;p&gt;Refer to my previous &lt;a href="http://paulhammant.com/blog/introducing-story-navigator.html"&gt;JBehave &amp;#8216;Story Navigator&amp;#8217;&lt;/a&gt; app again. Here&amp;#8217;s the &lt;span class="caps"&gt;URL&lt;/span&gt; to the site, if you wanted to play with it, or look at it with Firebug:&lt;/p&gt;
&lt;p&gt;http://paul-hammant.github.com/StoryNavigator/navigator.html&lt;/p&gt;
&lt;p&gt;What I want to be able to do is use the model references to locate elements within the page.  The most &amp;#8216;sophisticated&amp;#8217; example is the &amp;#8220;Showing 3 Of 3 Stories&amp;#8221;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/showing_3_of_3.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;That looks like this in the source:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;section&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Stories&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stories-total&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Showing {{data.xref.stories.$filter(search).$size()}} of {{data.xref.stories.$size()}} stories&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And in the rendered &lt;span class="caps"&gt;DOM&lt;/span&gt;, that looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/showing_3_of_3_dom.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Getting to the first number should be as simple as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;//span[@ng:bind = &amp;#39;data.xref.stories.$filter(search).$size()&amp;#39;] 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The problem is that $ is an illegal char in respect of a xpath lookup inside Firefox.  I&amp;#8217;ve tried a couple of escape sequences for it, but have not been able to overcome that. Simpler model references like &amp;#8220;search.description&amp;#8221; should work though.&lt;/p&gt;
&lt;p&gt;The second, and perhaps more serious problem is that using xpath to find attributes &amp;#8220;ng:bind&amp;#8221; is impossible too. The colon is the issue, and despite there being advice on the StackOverflow as to how to search for namespace-centric things using xpath, none seem to work in Firefox.  This one should work, but does not:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;//span[local-name()=&amp;#39;bind&amp;#39; and namespace-uri()=&amp;#39;http://angularjs.org&amp;#39;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;OK, I think the AngularJS team needs to get inventive here.  Perhaps, for a certain mode of operation it should generate legal IDs for each element that are data-bound.  The IDs could be as is, munged somewhat to replace &amp;#8216;illegal&amp;#8217; chars, or a hash function of the string (or part of it).  It&amp;#8217;d only be used by Selenium, and the mode would not be turned on for production deployments of the app.  This dogged us previously with &lt;span class="caps"&gt;GWT&lt;/span&gt; applications where the &lt;span class="caps"&gt;XHTML&lt;/span&gt; was very sopisticated and locators for widgets required a PhD to make elegant.  For a client today, I&amp;#8217;m writing Selnium2 scripts for an app that has duplicate IDs, so I don&amp;#8217;t partocularly care whether, for testing, IDs are duplicated.  Selenium&amp;#8217;s Firefox or InternetExplorer driving can handle that.&lt;/p&gt;
&lt;p&gt;Anyway, I can get Selenium working with the Story Navigator app.  Here&amp;#8217;s some groovy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@Grapes&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium:selenium-java:2.20.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium.fluent:fluent-selenium:1.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.hamcrest:hamcrest-all:1.1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
 &lt;span class="nd"&gt;@GrabExclude&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nl"&gt;apis:&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apis&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;])&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.firefox.FirefoxDriver&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.StaleElementReferenceException&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openqa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.seleniumhq.selenium.fluent.FluentWebDriverImpl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seleniumhq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FirefoxDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;fluent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FluentWebDriverImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://paul-hammant.github.com/StoryNavigator/navigator.html&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;within&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stories-total&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Showing 3 Of 3 Stories&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;search.description&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Hat&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;fluent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;within&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stories-total&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;within&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Showing 1 Of 3 Stories&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s the resulting video, which is too quick to see (less than &lt;strong&gt;half a second&lt;/strong&gt; for the actual browser interaction):&lt;/p&gt;
&lt;p&gt;&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/Aj1YRhJvw6U" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;You might have to advance the video frame by frame in order to see it working &amp;#8211; sorry!&lt;/p&gt;
&lt;p&gt;One more small fact &amp;#8211; Angular has about a 50ms delay between events in the page (I type &amp;#8220;hat&amp;#8221;) and the model-view-controller results of those changes.  Humans cannot see that, but Selenium can, hence the build in delays.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/CUAekP96Tso" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/03/15/angular-and-selenium-again/</feedburner:origLink></item><item><title>The Importance of Design-Mode for Client-Side MVC</title><link>http://feedproxy.google.com/~r/paulhammant/~3/tv1rjO1O0Wk/</link><pubDate>Mon, 12 Mar 2012 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/03/12/the-importance-of-design-mode-for-client-side-mvc</guid><description>&lt;p&gt;Synopsis:  Design mode (the same view but without Angular or Knockout loaded) is a great way to see the multiplicity of the view as you develop.&lt;/p&gt;
&lt;p&gt;Remember that &lt;a href="http://paulhammant.com/blog/introducing-story-navigator.html"&gt;JBehave &amp;#8216;Story Navigator&amp;#8217;&lt;/a&gt; blog entry I did a year ago?  Well, give it a quick click to remind yourself.  It has been tidied up some since &amp;#8211; as I get better with Angular.  I forked it on github, to a version that&amp;#8217;s published in the same way (github pages) but without JavaScript running; No AngularJS specifically.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a screenshot:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/sn_wo_js.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;And here&amp;#8217;s the link, which is a better way of quickly seeing the shrunken picture above.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://paul-hammant-fork.github.com/StoryNavigator/navigator.html"&gt;http://paul-hammant-fork.github.com/StoryNavigator/navigator.html&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Moustache or handlebar style template fields everywhere.&lt;/h2&gt;
&lt;p&gt;You can see them everywhere &amp;#8211; things that would be output text in the final version, but are now just model references in design mode.  Some are simple field references, some are more compex expressions, or have formatters attached.&lt;/p&gt;
&lt;h2&gt;Only one row versus many in the running version.&lt;/h2&gt;
&lt;p&gt;There are three rows in the running version, and only one row in the design mode.&lt;/p&gt;
&lt;h2&gt;Multiple alternates concurrently shown in design-mode&lt;/h2&gt;
&lt;p&gt;The passed &lt;strong&gt;OR&lt;/strong&gt; failed icon in in the running version, is now passed &lt;strong&gt;&lt;span class="caps"&gt;AND&lt;/span&gt;&lt;/strong&gt; failed (side by side) in design mode:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/passed_and_failed.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;For each column in the table, according the the design of the JBehave StoryNavigator, there are three possibilities in respect of sort order:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/three-statii.png" alt="" /&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;this column is the sorted one in descending order&lt;/li&gt;
	&lt;li&gt;this column is the sorted one in ascending order&lt;/li&gt;
	&lt;li&gt;this column is not the one being sorted&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three of those realities are shown for each column, and it is that which forces the columns far out to the right of the view.  There&amp;#8217;s an obvious improvement that someone could make to that &amp;#8211; as there is no need for me to repeat the text of the column name as it does not change.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Hopefully you get to see the beauty of of Angular and Knockout: You get to see a design preview of the finished page, with all of the components visible, and with &lt;span class="caps"&gt;CSS&lt;/span&gt; hooked up as it would be in the finished product.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/tv1rjO1O0Wk" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/03/12/the-importance-of-design-mode-for-client-side-mvc/</feedburner:origLink></item><item><title>Replacing JQuery With Angular</title><link>http://feedproxy.google.com/~r/paulhammant/~3/7J2R57qGOhc/</link><pubDate>Sat, 03 Mar 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/03/03/replacing-jquery-with-angular</guid><description>&lt;p&gt;Synopsis:  Angular webapps can have a lot less code that JQuery ones, and be easier to follow.&lt;/p&gt;
&lt;p&gt;I took the source of an e-zine article from 2007 using an older version of JQuery, checked it into to Github and incrementally converted it to Angular.  I&amp;#8217;ve squashed that series of commits into on to make it easier on the eye (and hide all the mistakes I made en route), and you &lt;a href="https://github.com/paul-hammant/angular_air/commit/c398a956709c326336b0feb541fac8e517b76653"&gt;can see that here&lt;/a&gt;.  It is worth perusing that commit for a few moments to see the comparison, and the &lt;strong&gt;shear weight of code deleted&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The original version (JQuery 1.2)&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/jquery_air.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.digital-web.com/articles/jquery_crash_course/"&gt;Digital Web&amp;#8217;s article supporting the JQuery version&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.digital-web.com/extras/jquery_crash_course/"&gt;A live version of that JQuery demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Please ignore the fact that nobody in real life would actually commission a seat plan app with this functionality.&lt;/p&gt;
&lt;h2&gt;After Refactoring to Angular 0.9.19&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/angular_air.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://paul-hammant.github.com/angular_air/"&gt;Play with a live version of that Angular_Air demo&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;What did I change?&lt;/h2&gt;
&lt;p&gt;I replaced JQuery with Angular of course.  The &lt;span class="caps"&gt;HTML&lt;/span&gt; file was huge before, but is now 95% smaller because I&amp;#8217;m using the &lt;code&gt;ng:repeat&lt;/code&gt; to repeat the rows/seats through the airplane map, and also the passengers table below.  The JQuery using JavaScript (global.js) has been entirely deleted (as has JQuery), I have new JavaScript in the &lt;span class="caps"&gt;HTML&lt;/span&gt; page for the model setup, and a couple of functions I&amp;#8217;m calling to support the view.  That&amp;#8217;s about an 80% JavaScript reduction.&lt;/p&gt;
&lt;p&gt;I changed the design of the passenger table.  It used to be a tabbed dialog, with which row you were viewing changing in its entirety each time you clicked a row in the airplane.  Now, it shows all the individual seats you have selected, in an expanding table.  Perhaps that should be constrained in terms of height and have a scroll bar.  In the older version, the passenger details for selected seats were highlighted in yellow, and editable, whereas the unselected passengers were not yellow, and not editable.  In my version, the unselected passengers are not shown (see use of &lt;code&gt;ng:show&lt;/code&gt; in the &lt;span class="caps"&gt;HTML&lt;/span&gt; source)&lt;/p&gt;
&lt;p&gt;The Angular version is much easier to understand. Perhaps even easy enough for relative novices. That would not be true for a Backbone redux of the same app. The JavaScript models for backbone I&amp;#8217;m beginning to think are &lt;span class="caps"&gt;MORE&lt;/span&gt; &lt;span class="caps"&gt;VERBOSE&lt;/span&gt; than the Java equivalent for &lt;span class="caps"&gt;GSON&lt;/span&gt; / Jackson serialization.  A Knockout version of the same would be roughly the same implementation and lines of code to the Angular one.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve also appended a debug panel, that shows the two pertinent model objects in the app.  You can see that live updating as you click around in the airplane image, and the passenger details.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s less than perfect?&lt;/h2&gt;
&lt;p&gt;The model is less than perfect to me.  What I&amp;#8217;d like to have :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;seats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; 
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;passenger_name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Fred&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;passenger_name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Wilma&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// etc    &lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;seats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;positions&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;posn&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;passenger_name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Fred&amp;quot;&lt;/span&gt; 
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;posn&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;passenger_name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Wilma&amp;quot;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;// etc    &lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;But what I have instead is something less hierarchical:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;seats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
	&lt;span class="nx"&gt;posn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;passenger_name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Fred&amp;quot;&lt;/span&gt; 
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
	&lt;span class="nx"&gt;posn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;b&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;passenger_name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Wilma&amp;quot;&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// etc&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The reason I&amp;#8217;ve had to compromise that the seats are rendered from a &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; tag within the &lt;span class="caps"&gt;HTML&lt;/span&gt;, and the are no grouping expressed within that list of 25 &amp;#215; 6 seat permutations.  Un-orderd lists don&amp;#8217;t have the ability to support groupings in a way that would be legal for &lt;span class="caps"&gt;HTML&lt;/span&gt;. The model in Angular has to support the view, and if it does not, then it has to change. Thus I flattened it.  I would refactor my backend logic to be able to serve that up.  If the app were to send the whole seat plan back to the server to persist it, I&amp;#8217;d change that handling of that too, to support a receiving flatter seat list.&lt;/p&gt;
&lt;h2&gt;Final thoughs&lt;/h2&gt;
&lt;p&gt;JQuery has moved on quite a bit since 2007.  The core library itself has improved, but there has also been a slew of controls in the &amp;#8220;JQuery UI&amp;#8221; space.  Lastly both &lt;a href="http://handlebarsjs.com/"&gt;Handlebars&lt;/a&gt; and &lt;a href="http://javascriptmvc.com/"&gt;JavaScriptMVC&lt;/a&gt; are technologies that independently pushed the capabilities of JQuery in the directions I&amp;#8217;m talking about here.  With a mix of these, code reductions towards that I&amp;#8217;ve achieved above, are possible.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/7J2R57qGOhc" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/03/03/replacing-jquery-with-angular/</feedburner:origLink></item><item><title>Client-Side MVC: browser plugins</title><link>http://feedproxy.google.com/~r/paulhammant/~3/Gb1BBzW8EPE/</link><pubDate>Tue, 14 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/14/client-side-mvc-browser-plugins</guid><description>&lt;p&gt;I&amp;#8217;ll do a quick bit of blue-sky thinking.  The range of ultimate &amp;#8216;winner(s)&amp;#8217; of the Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt; frameworks shakedown, could do worse than have sympathetic plugins for developers to use. I can think of two things that would help developers.&lt;/p&gt;
&lt;h2&gt;View Source (on Angular source):&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/source_plugin.jpg" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;View in Page (after some hot-key is pressed):&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/view_plugin.jpg" alt="" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/Gb1BBzW8EPE" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/14/client-side-mvc-browser-plugins/</feedburner:origLink></item><item><title>Client-Side MVC frameworks compared</title><link>http://feedproxy.google.com/~r/paulhammant/~3/qCUAzAn3ILE/</link><pubDate>Mon, 13 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/13/client-side-mvc-frameworks-compared</guid><description>&lt;p&gt;A month ago Gordon L. Hempton &lt;a href="http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed"&gt;wrote about twelve JavaScript frameworks&lt;/a&gt; in the Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt; space.  His rating criteria were different to mine.  One that really sticks out is that I like the logic not forcing the template &lt;span class="caps"&gt;HTML&lt;/span&gt; to migrate to &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags. Depending on the sophistication of the app, I like to be able to see the app in a browser or DreamWeaver when the framework &lt;b&gt;is not running&lt;/b&gt;. It gives me a way of gauging the composition of the app.  It appeals to a &lt;a href="http://en.wikipedia.org/wiki/WYSIWYG"&gt;&lt;span class="caps"&gt;WYSIWYG&lt;/span&gt;&lt;/a&gt; leaning that I have. I like my UI frameworks to be &lt;i&gt;built for designability&lt;/i&gt; if you like.&lt;/p&gt;
&lt;p&gt;Addy Osmani has a number of implementation of a &lt;span class="caps"&gt;TODO&lt;/span&gt; app on a &lt;a href="http://addyosmani.github.com/todomvc/"&gt;github pages site&lt;/a&gt;. For composition purposes, this really is the definitive place presently.  Using those, I&amp;#8217;m going to scrutinize the &lt;span class="caps"&gt;HTML&lt;/span&gt; and how it the app looks without JavaScript.  I checked out Addy&amp;#8217;s repo, then recursively deleted all javascript files, before loading the main page for each into a browser.&lt;/p&gt;
&lt;h2&gt;Angular&lt;/h2&gt;
&lt;p&gt;In the browser:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_angular.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;In DreamWeaver:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_angular_dw.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;What I like is that I can see the repeating item in it&amp;#8217;s mustache templating style: &amp;#123;&amp;#123;todo.content&amp;#125;&amp;#125;.  What I don&amp;#8217;t like is that I can&amp;#8217;t see the &amp;#8220;X item(s) left&amp;#8221; message, that live updates.  Angular leaves your template &lt;span class="caps"&gt;HTML&lt;/span&gt; in-situ &amp;#8211; where it would be if there were no angular logic happening to it.  Here&amp;#8217;s the todo list in code (&lt;code&gt;ng:repeat&lt;/code&gt; is the looping construct):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todos&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-list&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;#39;editing-&amp;#39; + todo.editing + &amp;#39; done-&amp;#39; + todo.done&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo in todos&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.done&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/ &amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;editTodo(todo)&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-content&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{ todo.content }} &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-destroy&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;removeTodo(todo)&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;ng:submit=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;finishEditing(todo)&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-input&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;my:focus=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.editing&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;my:blur=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;finishEditing(todo)&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.content&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Backbone&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_backbone.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Missing is the repeating TODOs.  Instead, in the page, there is a &lt;code&gt;&amp;lt;ul id="todo-list"&amp;gt;&amp;lt;/ul&amp;gt;&lt;/code&gt;  placeholder for Backbone to insert child elements to later. Here&amp;#8217;s the template, in canonical Backbone (hidden inside a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;	&lt;span class="c"&gt;&amp;lt;!-- Templates --&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/template&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-template&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo &amp;lt;%= done ? &amp;#39;done&amp;#39; : &amp;#39;&amp;#39; %&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;checked=&amp;quot;checked&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/label&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-destroy&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-input&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;%= content %&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There&amp;#8217;s no &lt;code&gt;ng:repeat&lt;/code&gt; equivalent code in the &lt;span class="caps"&gt;HTML&lt;/span&gt;, that&amp;#8217;s done in JavaScript.&lt;/p&gt;
&lt;h2&gt;Knockout&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_knockout.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not sure if the way the &lt;span class="caps"&gt;TODO&lt;/span&gt; app was coded with knockout is the only way, but the line that would show an individual &lt;span class="caps"&gt;TODO&lt;/span&gt; item from the list of TODOs looks like &lt;code&gt;&amp;lt;div class="todo-content" data-bind="text: content, event: { dblclick: edit }" style="cursor: pointer;"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;.  Without the frameworks attached, there&amp;#8217;s nothing to see for that &lt;span class="caps"&gt;DIV&lt;/span&gt;.  If I hard-code &lt;code&gt;TODO-CONTENT&lt;/code&gt; in that &lt;span class="caps"&gt;DIV&lt;/span&gt;, and reload, then it looks more like Angular:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_knockout2.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;It is nice to see the inline templating rather than the template in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag like Angular:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todos&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;visible: todos().length&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;check-all&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checked: allCompleted&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;check-all&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Mark all as complete&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-list&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foreach: todos&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;css: { editing: editing }&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;css: { done : done }&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checked: done&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-content&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text: content, event: { dblclick: edit }&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cursor: pointer;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-destroy&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;click: $root.remove&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-input&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-bind=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;value: content, valueUpdate: &amp;#39;afterkeydown&amp;#39;, enterKey: stopEditing, event: { blur: stopEditing }&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Knockback&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; as Backbone&amp;#8217;s&lt;/p&gt;
&lt;p&gt;Knockback is a very pragmatic merger of the best features of Backbone and Knockout.  Unlike many of the websites for these technologies, Knockback&amp;#8217;s site tries to instantly sell the technology to the developer.  Like Backbone, Knockback keeps templates separate.  Here&amp;#8217;s their todo list: &lt;code&gt;&amp;lt;ul class="todo-list" data-bind="template: {name: 'item-template', foreach: todo_list.todos}"&amp;gt;&amp;lt;/ul&amp;gt;&lt;/code&gt;.  Here is the template for that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/x-jquery-tmpl&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-template&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;css: {done: done, editing: edit_mode}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checked: done&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-text&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text: text, dblclick: toggleEditMode&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-destroy&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;click: destroyTodo&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-input&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value: text, event: {keyup: onEnterEndEdit}&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Broke&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; as Backbone&amp;#8217;s&lt;/p&gt;
&lt;p&gt;The template logic is in JavaScript files, which I really don&amp;#8217;t like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;templates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;ul class=&amp;quot;items&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;{% for task in taskList %}&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="nx"&gt;genericTaskTemplate&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;{% endfor %}&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;&amp;lt;/ul&amp;gt;&amp;#39;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;genericTaskTemplate&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;li class=&amp;quot;item&amp;quot; data-app_label=&amp;quot;{{ task.__class__._meta.appLabel }}&amp;quot; data-model=&amp;quot;{{ task.__class__._meta.modelName }}&amp;quot; data-pk=&amp;quot;{{ task.pk }}&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;&amp;lt;form action=&amp;quot;#/task/update/{{ task.pk }}/&amp;quot;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;title&amp;quot; value=&amp;quot;{{ task.title }}&amp;quot; /&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;&amp;lt;/form&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Sammy&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; as Backbone&amp;#8217;s&lt;/p&gt;
&lt;p&gt;The template logic is in separate .template file, which seems unnecessary given it&amp;#8217;s just an extended &lt;span class="caps"&gt;HTML&lt;/span&gt; format.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;data-type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;list&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;%= list.id %&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;% $.each(todos, function(index, todo) { %&amp;gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;data-type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;%= todo.id %&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;%= todo.done ? &amp;#39;done&amp;#39; : &amp;#39;&amp;#39; %&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;done&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;/&amp;gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;trashcan&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;%= todo.id %&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;contenteditable=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;data-id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;%= todo.id %&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-item&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;%= todo.name %&amp;gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;% }); %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Ember&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_ember.jpg" alt="" /&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/x-handlebars&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todos&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-list&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;contentBinding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Todos.todosController&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;tagName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ul&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;itemClassBinding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;content.isDone&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="nx"&gt;Ember&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Checkbox&lt;/span&gt; &lt;span class="nx"&gt;titleBinding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;content.title&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;valueBinding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;content.isDone&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;/collection}}&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;/view}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You should know by now that I&amp;#8217;m really going to like Ember&amp;#8217;s templating. From my point of view this one is in last position!&lt;/p&gt;
&lt;h2&gt;ExtJs&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; as Backbone&amp;#8217;s&lt;/p&gt;
&lt;p&gt;The template logic is in JavaScript files.  ExtJs is not so much &lt;span class="caps"&gt;HTML&lt;/span&gt; augmented with Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt; capability like the rest are.  It&amp;#8217;s more of a departure from &lt;span class="caps"&gt;HTML&lt;/span&gt; and a reality built on it&amp;#8217;s own &lt;span class="caps"&gt;DSL&lt;/span&gt; which can be highly attractive in its own right.  I&amp;#8217;m only including it because Addy has it on his list.  In this case, the ExtJS grammar is showing inlined &lt;span class="caps"&gt;HTML&lt;/span&gt; in the pertinent list of TODOs section:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;Ext.define(&amp;#39;Todo.view.TaskList&amp;#39; , {
    store: &amp;#39;Tasks&amp;#39;,
    loadMask: false,
    itemSelector: &amp;#39;div.row&amp;#39;,
    extend: &amp;#39;Ext.view.View&amp;#39;,
    alias : &amp;#39;widget.taskList&amp;#39;,
    tpl: Ext.create(&amp;#39;Ext.XTemplate&amp;#39;,
        &amp;#39;&lt;span class="nt"&gt;&amp;lt;tpl&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;#39;,
            &amp;#39;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;row&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&amp;#39;,
                &amp;#39;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;{[&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checked&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;&amp;quot;&amp;quot;]}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;&amp;#39;,
                &amp;#39;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{[values.checked ? &amp;quot;&lt;/span&gt;&lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;:&lt;/span&gt; &lt;span class="err"&gt;&amp;quot;&amp;quot;]}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{label}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;&amp;#39;,
            &amp;#39;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&amp;#39;,
        &amp;#39;&lt;span class="nt"&gt;&amp;lt;/tpl&amp;gt;&lt;/span&gt;&amp;#39;,
        {compiled: true}
    )
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Fidel&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; as Backbone&amp;#8217;s&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/template&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-template&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo {!= todo.done ? &amp;#39;done&amp;#39; : &amp;#39;&amp;#39; !}&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;todoid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{!= todo.guid !}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;check&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;checked=&amp;quot;checked&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-content&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;destroyTodo&amp;quot;&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-destroy&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-input&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Like Backbone, or this example, there&amp;#8217;s no &lt;code&gt;ng:repeat&lt;/code&gt; equivalent code in the &lt;span class="caps"&gt;HTML&lt;/span&gt;. It is done in JavaScript instead.&lt;/p&gt;
&lt;h2&gt;JavaScriptMVC&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; like Backbone&amp;#8217;s.  Template nesting (inside &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags) looks powerful:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;text/ejs&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;todosEJS&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
		&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&amp;gt;&lt;/span&gt;
		&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;todoEJS&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
		&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;text/ejs&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;todoEJS&amp;#39;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;checkbox&amp;#39;&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;complete&amp;#39;&lt;/span&gt; 
		&lt;span class="o"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;checked&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&amp;gt;&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;%=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;empty todo ...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;%&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;destroy&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;JQuery with Handlebars&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; like Backbone&amp;#8217;s.  Separate template again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/x-handlebars-template&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-template&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;done&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;/if}} data-id=&amp;quot;{{id}}&amp;quot;&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;view&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
		&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;toggle&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;/if}}&amp;gt;&lt;/span&gt;
		&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/label&amp;gt;&lt;/span&gt;
		&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;destroy&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/a&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
	&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{{title}}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/li&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;/this}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Spine&lt;/h2&gt;
&lt;p&gt;Screenshot &amp;#8211; as Backbone&amp;#8217;s. Template again leverages &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; again but is inline with the &lt;span class="caps"&gt;HTML&lt;/span&gt; that contains it.  Looping is not in the grammar though, so is performed by JavaScript as the template is acted on.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;items&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/html&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;task-template&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;item {{if done}}done{{/if}}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;view&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Double click to edit...&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;/if}}&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt; &amp;lt;a class=&amp;quot;destroy&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;edit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;${name}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt; Library&lt;/h2&gt;
&lt;p&gt;Screenshot as Backbone&amp;#8217;s. As is the norm the template is in a &lt;code&gt;&amp;amp;lt;script&amp;gt;&lt;/code&gt; tag, with the looping in Javascript.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/x-template&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-item-template&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-view&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checkbox&amp;quot;&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-checkbox&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-content&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;tabindex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-edit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-input&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{text}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#&amp;quot;&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-remove&amp;quot;&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Remove this task&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;todo-remove-icon&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Batman&lt;/h2&gt;
&lt;p&gt;(added 14 Feb)&lt;/p&gt;
&lt;p&gt;Batman&amp;#8217;s &lt;span class="caps"&gt;TODO&lt;/span&gt; is in it&amp;#8217;s own &lt;a href="https://github.com/Shopify/batman/blob/master/examples/alfred.html"&gt;Github Repo&lt;/a&gt;, and not Addy&amp;#8217;s one (yet).  Batman uses CoffeeScript instead of JavaScript, which is alluring in its own right.  Here&amp;#8217;s the screenshot:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_batman_asis.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Like Knockout, the bound field is not visible in design mode.  Similarly, the number of items statistic is also missing.  Lastly the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag for &amp;#8216;delete&amp;#8217; is missing a and thus does not appear as a link. The other frameworks were using fancy images for this, so that is probably the same for them too, but not apparent in their demos. I&amp;#8217;ve hacked those things in (hard-coded) and taken another screenshot for your enjoyment:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_batman_mods.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The good news (to me) is that the Batman extends &lt;span class="caps"&gt;HTML&lt;/span&gt; like Angular and Knockout.&lt;/p&gt;
&lt;h2&gt;Conclusion.&lt;/h2&gt;
&lt;p&gt;Only Angular, Knockout and Batman allow me to see what&amp;#8217;s what in design mode, and see the mechanism of looping in the extended &lt;span class="caps"&gt;HTML&lt;/span&gt; grammar.  That&amp;#8217;s the opposite of one of Gordon L. Hempton&amp;#8217;s &amp;#8220;good&amp;#8221; criteria.&lt;/p&gt;
&lt;p&gt;It is also interesting that one of these has Microsoft patronage, and another has Google support.  I wonder if there&amp;#8217;s a Dart port &amp;#8220;Dangular&amp;#8221; in the future from the Google fellows :-P&lt;/p&gt;
&lt;h2&gt;After thought.&lt;/h2&gt;
&lt;p&gt;Angular&amp;#8217;s &lt;span class="caps"&gt;TODO&lt;/span&gt; list implementation could be even better in design mode, if it encoded stats like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-stats&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-count&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:show=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hasTodos()&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
	{{statsCount()}} item{{statsPlural()}} left.
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-clear&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:show=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hasFinishedTodos()&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;clearCompletedItems()&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Clear {{finishedTodos()}} completed item{{finsihedPlural()}}
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Instead of the way it is now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-stats&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-count&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:show=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hasTodos()&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ng:pluralize&lt;/span&gt; &lt;span class="na"&gt;count=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;remainingTodos()&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;when=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{&amp;#39;0&amp;#39; : &amp;#39;No items left.&amp;#39;, &amp;#39;1&amp;#39;: &amp;#39;1 item left.&amp;#39;, &amp;#39;other&amp;#39; : &amp;#39;{} items left.&amp;#39; }&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ng:pluralize&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo-clear&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:show=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hasFinishedTodos()&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;clearCompletedItems()&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Clear &lt;span class="nt"&gt;&amp;lt;ng:pluralize&lt;/span&gt; &lt;span class="na"&gt;count=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;finishedTodos()&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;when=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{&amp;#39;1&amp;#39;: &amp;#39;1 completed item&amp;#39;, &amp;#39;other&amp;#39; : &amp;#39;{} completed items&amp;#39; }&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ng:pluralize&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We&amp;#8217;d get a design mode view like so:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/todo_angular_better.jpg" alt="" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/qCUAzAn3ILE" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/13/client-side-mvc-frameworks-compared/</feedburner:origLink></item><item><title>Client-Side-MVC applications : best practice (part 1)</title><link>http://feedproxy.google.com/~r/paulhammant/~3/CFa1O2agbx8/</link><pubDate>Fri, 10 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/10/client-side-mvc-applications-best-practice-part-1</guid><description>&lt;p&gt;I&amp;#8217;ve made a quick game &amp;#8211; &amp;#8220;Whack a Mole &amp;#8211; &lt;i&gt;Angular Edition&lt;/i&gt;&amp;#8221;.  Try clicking on the moles or holes:&lt;/p&gt;
&lt;p&gt;&lt;iframe style="text-align:left; width:450px; height:340px; background-color:transparent;" src="http://paul-hammant.github.com/whack_a_mole_angular/"&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;Hmm, the mole image has some lasso artifacts after I &lt;a href="http://en.wikipedia.org/wiki/Mole_(animal)"&gt;reappropriated it from wikipedia&lt;/a&gt;. Damn you CorelPaint!&lt;/p&gt;
&lt;p&gt;Now, look at the source (it&amp;#8217;s in an iframe so you&amp;#8217;ll see the minimum amount of &lt;span class="caps"&gt;HTML&lt;/span&gt;). Of if you trust me not to do some sleight of hand, you can look at the &lt;a href="https://github.com/paul-hammant/whack_a_mole_angular/blob/gh-pages/index.html"&gt;github source for that&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Aside from the 2 &amp;#215; 2 &lt;span class="caps"&gt;JSON&lt;/span&gt; &amp;#8220;moles&amp;#8221; document with a not-so-random &amp;#8220;up&amp;#8221; versus &amp;#8220;down&amp;#8221; state, the angular magic is:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/wam_1.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The thing I want folks to really grok right now, is that there are two images in the source apparently side by side within each of the TD elements.  Specifically &amp;#8220;mole up&amp;#8221; and &amp;#8220;mole down&amp;#8221;.  It is Angular that&amp;#8217;s making one one of the two show at any one moment in time.  Gecko and Webkit are fast enough to not make it apparent that there is momentarily two or none of the images visible for a cell.  Perhaps Angular has no such tweening state, and is even in charge of the browser repaints.  The &lt;code&gt;ng:click&lt;/code&gt; stuff is where the model gets changed &amp;#8211; or rather the &amp;#8220;up&amp;#8221; becomes &amp;#8220;down&amp;#8221; or vice versa. &lt;code&gt;ng:click&lt;/code&gt; is attachable to any meaningful element of course.&lt;/p&gt;
&lt;p&gt;Here is what the source looks like in DreamWeaver&amp;#8217;s &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt; mode for the single apparent cell before Angular re-does the &lt;span class="caps"&gt;DOM&lt;/span&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/wam_dreamweaver.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;Yes that&amp;#8217;s the mole both apparently &lt;b&gt;alive&lt;/b&gt; and &lt;b&gt;dead&lt;/b&gt; at the same time&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s an alternate way of doing the same thing without the two image and &lt;code&gt;ng:show&lt;/code&gt; &amp;amp; &lt;code&gt;ng:hide&lt;/code&gt; trick, that I would count as a refactoring:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/wam_2.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Whichever way you do it, the model drives the view (no JavaScript to shuttle back and forth values).  And the view mutates the model &amp;#8211; in this case via expressions co-located with &lt;span class="caps"&gt;HTML&lt;/span&gt;.  The Angular guys suggest that these things are better done with real JavaScript functions that can touch the model, and they are right as far as testability is concerned.  But there&amp;#8217;s a &lt;a href="http://www.pretotyping.org/"&gt;preotyping&lt;/a&gt; community that&amp;#8217;s &lt;i&gt;really&lt;/i&gt; going to dig Angular.  There&amp;#8217;s also a the likes of User Experience (UX) and Business Analysts folks who may like to join in somewhat in the actual development of functionality.&lt;/p&gt;
&lt;p&gt;It is interesting too, what Angular does to the &lt;span class="caps"&gt;DOM&lt;/span&gt; at runtime. Below is a screen-shot from Firebug.  The ghosted lines are not visible in the browser frame. Thus it&amp;#8217;s conformation (if you needed it) that only one image is visible at a time for one cell:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/wam_dom.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;Business apps&lt;/h2&gt;
&lt;p&gt;You&amp;#8217;re going to be able to compose larger applications fairly easily, and leverage plenty of model following UI changing activities.  You&amp;#8217;re principal duty is to keep the model supporting the view, and refactoring as appropriate to fit the growing vision.  The hard stuff, you will still do in JavaScript, but I think that&amp;#8217;s the exception rather than the rule, as the Angular makes it all pretty easy.&lt;/p&gt;
&lt;p&gt;Consider a table of fruit in a page:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/fruits_1.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;If clicked, a line could expand a little to accommodate more text:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/fruits_2.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;HTML&amp;#8217;s &lt;b&gt;rowspan&lt;/b&gt; might be the Angular triggered UI trick to accommodate that, by the &lt;code&gt;ng:click&lt;/code&gt; would trigger an &lt;code&gt;'expanded = true'&lt;/code&gt; model change for the fruit in question. As retrieved from the database the fruit never had a &lt;code&gt;'expanded = false'&lt;/code&gt; node, let alone &lt;code&gt;'expanded = true'&lt;/code&gt;, but that does not matter as in the worse case scenario, your could ignore such properties when attempting to write the fruits back to the database (if they were editable in-situ).  Your &lt;i&gt;original&lt;/i&gt; &lt;span class="caps"&gt;JSON&lt;/span&gt; document to support the list of fruit might only have enough text to support the collapsed list of fruit.  If that&amp;#8217;s the case, you&amp;#8217;d do an &lt;span class="caps"&gt;AJAX&lt;/span&gt; wire call to get the expanded fruit description, and populate an &amp;#8216;expandedFruit&amp;#8217; object outside the &amp;#8216;fruits&amp;#8217; model.  Only one fruit is expanded at a time, so it could easily be a single field.  Judicious use of &lt;code&gt;ng:show&lt;/code&gt; again will make the right control of two show within the &amp;lt;td&amp;gt; .. &amp;lt;/td&amp;gt; elements.&lt;/p&gt;
&lt;p&gt;Or some form of overlay could happen:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://paulhammant.com/images/fruits_3.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The same backend model, &lt;span class="caps"&gt;AJAX&lt;/span&gt; fetch, and &lt;code&gt;expandedText&lt;/code&gt; field, but a different view still driven by &lt;code&gt;ng:show&lt;/code&gt;.  You could refactor from one design to the other.  Indeed the UX person could do so without developer help.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;All of a sudden, web apps became simpler to make (again).&lt;/b&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/CFa1O2agbx8" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/10/client-side-mvc-applications-best-practice-part-1/</feedburner:origLink></item><item><title>The document is the single source of truth</title><link>http://feedproxy.google.com/~r/paulhammant/~3/6aWpIOnDIxw/</link><pubDate>Wed, 08 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/08/document-is-the-single-source-of-truth</guid><description>&lt;p&gt;I&amp;#8217;m inspired by the title of a blog entry that &lt;a href="http://www.decipherinc.com/n/blog/development-and-engineering-team/2011/04/angular-model-single-source-truth"&gt;mulls Angular&lt;/a&gt;. The title in question, for those that have not clicked is &lt;strong&gt;The model is the single source of truth&lt;/strong&gt;, and it refers to &lt;a href="http://en.wikipedia.org/wiki/Single_Source_of_Truth"&gt;Single Source of Truth&lt;/a&gt; concept, which is occasionally bandied around in enterprise computing.  Anyway, it came up today at work, so I thought I&amp;#8217;d blog.&lt;/p&gt;
&lt;h2&gt;Models in the normalized database era.&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://www.google.com/search?q=codd+and+date"&gt;Codd and Date&lt;/a&gt; gave us relational database theorems/designs in general and &lt;span class="caps"&gt;SQL&lt;/span&gt; in particular.  From the 80&amp;#8217;s through to the turn of the millennia, database design was the foremost consideration in application development.  The initial work was done the old fashioned way, but later tools like &lt;a href="http://erwin.com/products/modeler/"&gt;Erwin&lt;/a&gt; allowed us to super-achieve in the realm of database design, with &amp;#8220;physical&amp;#8221; and &amp;#8220;logical&amp;#8221; models mapped and workflowed to completion.  Outputs were &lt;span class="caps"&gt;DDL&lt;/span&gt; that teams could use to deploy databases and fill them with starting records (most likely reference data). Applications could be built on top of that, we were assured.&lt;/p&gt;
&lt;p&gt;The Agile software movement (2000 onwards), decided that source control was &lt;strong&gt;the&lt;/strong&gt; reference place for &lt;span class="caps"&gt;SQL&lt;/span&gt; tables/indexes/views, and so was happier to not use such modeling tools upfront.  If they wanted pretty-printed models, they were happier to generate them from actual deployed schemas. Having everything on one &lt;span class="caps"&gt;SCM&lt;/span&gt; tool for a single-click-build was the highly desirable thing, and table-shapes changed as needed according to the &amp;#8216;stories&amp;#8217; being developed in an iteration.  Later the tool-chains improved for each language, to allow table-shape delta scripts to be automatically generated for day to day use, or more importantly between production releases.  This would be to &amp;#8216;upgrade&amp;#8217; a database shape, or &amp;#8216;downgrade&amp;#8217; if a production push were lamented.  Really though, for Agileists, the conflict was in the between object modeling and relational-schema modeling. In the eyes of the Agileists the object model was much more important than the table design. But it was not the Agile community per se that drove the next change: to document stores.&lt;/p&gt;
&lt;h2&gt;Models in the Client-side &lt;span class="caps"&gt;MVC&lt;/span&gt; era.&lt;/h2&gt;
&lt;p&gt;So everyone gets by now that I love the client-side &lt;span class="caps"&gt;MVC&lt;/span&gt; frameworks, and think they are the future.  When it comes to data storage the obvious conclusion is that the backend should save something pretty close to the document that the client presents, mutates, and sends back to the server for posterity.  I don&amp;#8217;t though, as yet, think in &amp;#8220;models&amp;#8221; for the data. Instead I think in &lt;span class="caps"&gt;JSON&lt;/span&gt; documents.  Indeed, it is the representation on the wire for a &lt;span class="caps"&gt;GET&lt;/span&gt; to populate a page, and the &lt;span class="caps"&gt;PUT&lt;/span&gt; for a hypothetical return of a changed document that my minds-eye considers.  Ideally, the document&amp;#8217;s structure in those two scenarios should be pretty much the same.  Like for a normalized database in a previous era, there are one-to-many relationships etc, but everything for an canonical key is represented in one textual document.  Now a backend could split up such a &lt;span class="caps"&gt;JSON&lt;/span&gt; document in a &lt;span class="caps"&gt;PUT&lt;/span&gt; operation, and write/overwrite dozens of records in many tables (in one transaction), but why bother?  &lt;strong&gt;Use a document store instead&lt;/strong&gt;. When would you use a normalized DB design today? The answer to that is: &lt;strong&gt;only when you have other processes reading and writing to your database&lt;/strong&gt;.  For the most part these days, we&amp;#8217;re interfacing to other systems via service calls (Web Services, &lt;span class="caps"&gt;REST&lt;/span&gt; etc), and using yet more documents on Enterprise Service Buses (Tibco, &lt;span class="caps"&gt;JMS&lt;/span&gt;, etc). There is also the possibility of writing a reportable database later, as a compromise.&lt;/p&gt;
&lt;p&gt;The development-time economies for avoiding a formal normalized database are huge, and teams should be starting with document stores, and justifying &amp;#8220;why not&amp;#8221; if needs be.  In short folks, get up to speed with the NoSQL movement.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/6aWpIOnDIxw" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/08/document-is-the-single-source-of-truth/</feedburner:origLink></item><item><title>Web-app model quandary</title><link>http://feedproxy.google.com/~r/paulhammant/~3/YaA1vRV6IA4/</link><pubDate>Mon, 06 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/06/webapp-model-quandary</guid><description>&lt;p&gt;Web applications take effort to develop for the most part. Serving static pages is much easier.  Over the years the patterns and techniques to develop web-apps have changed, and so has the division between logic we write on the web-server tier versus logic in the browser. We now write much more logic in the browser, and indeed we&amp;#8217;re on the cusp of an era where &lt;a href="http://en.wikipedia.org/wiki/Model-view-controller" title="MVC"&gt;Model-View-Controller&lt;/a&gt; in side the browser is the dominant design.  Micro-web-frameworks have made that possible, and there are new problems to solve.&lt;/p&gt;
&lt;p&gt;In this blog entry, I outline the difficulties making solid web-apps where the model is being heavily mutated.  Earlier today &lt;a href="http://paulhammant.com/2012/02/06/previous-web-architectures"&gt;I mulled the technologies in play in 1993, 2000 and 2006 for enterprise web-applications&lt;/a&gt;, and this blog entry is intended to continue the cataloging of technologies to the current day.&lt;/p&gt;
&lt;h2&gt;2012: The Micro Web Framework era.&lt;/h2&gt;
&lt;p style="float:left;"&gt;&lt;img src="/images/web_2012.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll phrase this as a status quo for the technologies in question.&lt;/p&gt;
&lt;p&gt;Micro Web Frameworks are largely divorced from the back-end server technology that hosts the larger web app.  As such, we can consider them interoperble with many alternate backends. In that web-server tier, Rails is still an immensely popular technology, but there are new kids on the block: &lt;a href="http://nodejs.org/"&gt;NodeJS&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Grails_(framework)" title="Rails for Groovy"&gt;Grails&lt;/a&gt;, Scala&amp;#8217;s &lt;a href="http://liftweb.net/"&gt;Lift&lt;/a&gt; and &lt;a href="http://scala.playframework.org/"&gt;Play&lt;/a&gt;.  Rails has competition with &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;. It ate its previous competitor &amp;#8211; &lt;a href="http://weblog.rubyonrails.org/2008/12/23/merb-gets-merged-into-rails-3"&gt;Merb and Rails merged to form Ruby on Rails 3.0&lt;/a&gt;.  SpringMVC and the one of .Net equivalents are one more the more &amp;#8216;safe&amp;#8217; enterprise choices. WebSockets and &lt;span class="caps"&gt;HTML&lt;/span&gt; are the cutting edge. &lt;span class="caps"&gt;PHP&lt;/span&gt; is still an amazingly popular choice for startups of course.&lt;/p&gt;
&lt;p&gt;The frameworks competing in this space include &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone&lt;/a&gt;, &lt;a href="http://knockoutjs.com/"&gt;Knockout&lt;/a&gt;, &lt;a href="https://github.com/kmalakoff/knockback"&gt;KnockBack&lt;/a&gt;, &lt;a href="http://spinejs.com/"&gt;Spine&lt;/a&gt;, &lt;a href="http://emberjs.com/"&gt;Ember&lt;/a&gt;, &lt;a href="http://batmanjs.org/"&gt;BatMan&lt;/a&gt;, &lt;a href="http://angularjs.org/"&gt;Angular&lt;/a&gt;, &lt;a href="http://sammyjs.org/"&gt;Sammy&lt;/a&gt;, &lt;a href="http://javascriptmvc.com/"&gt;JavaScriptMVC&lt;/a&gt;, &lt;a href="http://developer.yahoo.com/yui/"&gt;YUILibrary&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/SproutCore"&gt;SproutCore&lt;/a&gt;, &lt;a href="https://github.com/brokenseal/broke-client"&gt;Broke&lt;/a&gt; and &lt;a href="https://github.com/jgallen23/fidel"&gt;Fidel&lt;/a&gt; which all ship as JavaScript scripts from 30K and upwards.  The genus kicked off in 2009, but the competition did not heat up until the middle of last year.  They are all variations on an &lt;span class="caps"&gt;MVC&lt;/span&gt; theme.  &lt;span class="caps"&gt;HTML&lt;/span&gt; markup is kept (and the tool-chains and techniques around that).  But the page is now able to have programmed series and other turing complete functionality with an absolute minimum of hand crafted JavaScript.  The libraries perform something akin to magic to rewrite the page according to &lt;span class="caps"&gt;JSON&lt;/span&gt; data sources and a meld of more functional enhancements to &lt;span class="caps"&gt;HTML&lt;/span&gt; (that the library recognizes), and  a spattering of JavaScript for the more advanced functions.&lt;/p&gt;
&lt;p&gt;Back in the web-server tier, the chore is now serializing to and from &lt;span class="caps"&gt;JSON&lt;/span&gt;.  Some languages have that built in, some require libraries.  Java&amp;#8217;s &lt;a href="http://xstream.codehaus.org"&gt;XStream&lt;/a&gt; falls by the wayside for Java as it is better transforming to &lt;span class="caps"&gt;JSON&lt;/span&gt; than from it, Luckily there is &lt;a href="http://code.google.com/p/google-gson"&gt;&lt;span class="caps"&gt;GSON&lt;/span&gt;&lt;/a&gt; and &lt;a href="http://jackson.codehaus.org"&gt;Jackson&lt;/a&gt;.  Indeed having classes that support specific hierarchical structures of &lt;span class="caps"&gt;JSON&lt;/span&gt; is the challenge.  The dynamic languages have the advantage of course.&lt;/p&gt;
&lt;h3&gt;But where should the dominant model be?&lt;/h3&gt;
&lt;p&gt;The shift in onus is from the model being (mostly) in the web-server tier, to there being no clarity about whether the model is there or in the browser tier.  This is the inherent problem right now.  A starting position for design (once you&amp;#8217;ve decided to go with a micro web framework) is to have the browser tier do all the &lt;span class="caps"&gt;MVC&lt;/span&gt; for the app one &amp;#8216;page&amp;#8217; at a time, and retrieve &lt;span class="caps"&gt;JSON&lt;/span&gt; from a vestige app-server as well as put it back when done.  That makes the back-end more of a document store, and thinner by necessity.  It is easy to see that the persistence technologies built to support such apps should naturally retrieve and save such documents rather than normalized sets of records.  Old fashioned DB technologies (Oracle, SQLServer, MySql) still have a place, but the direct page-supporting tables should be more like document stores, and have &lt;span class="caps"&gt;CLOB&lt;/span&gt; fields to hold &lt;span class="caps"&gt;JSON&lt;/span&gt;, with simpler key fields. You should be more drawn to the likes of &lt;a href="http://cassandra.apache.org"&gt;Cassandra&lt;/a&gt;, &lt;a href="http://www.mongodb.org"&gt;MongoDB&lt;/a&gt;, or &lt;a href="http://couchdb.apache.org"&gt;CouchDB&lt;/a&gt; really though.&lt;/p&gt;
&lt;p&gt;With these browser-based &lt;span class="caps"&gt;MVC&lt;/span&gt; designs, if the web-app tier is doing any business logic, it is validating data a second time, as one can&amp;#8217;t really trust what goes on in the browser with the likes of GreaseMonkey and Firebug around.  If it finds validation issues, it is more than likely to send that back to the browser for re-presentation. An easy way to do that would be to embellish the document received with errata, and have the &lt;span class="caps"&gt;MVC&lt;/span&gt; app just display them inline. More about that in a follow up article.&lt;/p&gt;
&lt;p&gt;Things get more complicated when a larger document is served to a &lt;span class="caps"&gt;HTML&lt;/span&gt; page, but only smaller sections may be changed at a time.  There&amp;#8217;s a temptation to &lt;span class="caps"&gt;POST&lt;/span&gt; back the subsection, have the backend validate/persist that, and simultaneously rework the model in the browser to reflect what &amp;#8220;would have been served&amp;#8221; for the whole document.  Why not &lt;span class="caps"&gt;PUT&lt;/span&gt; the whole document to the backend, and reload if needed?  If the document is as big as a word document, and your app functions like GoogleDoc&amp;#8217;s word processor, then it would die with the overhead of the whole document going up and down per keystroke.  Google, has a very efficient wire language to describe the keystrokes being made in a &lt;span class="caps"&gt;POST&lt;/span&gt; operation labelled &amp;#8216;mutate&amp;#8217;. It hints that their model is &lt;strong&gt;mirrored&lt;/strong&gt; to both sides of the wire. It has to be really, as other people can concurrently edit Google Docs.&lt;/p&gt;
&lt;p&gt;The GoogleDocs design should make us hope for safety and transactionality with apps that use micro-web-frameworks.  The Page could be alive a long time without pushing data back to the web-tier and hence the data storage.  A beautiful aspect of &lt;span class="caps"&gt;MVC&lt;/span&gt; is the fact that you can in fact abandon (garbage collect if you like) the model, if the changes it holds is no longer required.  The older data-binding style of models is much harder to coerce into abandoning DB writes if required.  GoogleDocs allows the rolling back of a series of changes.  It feels more like a source control system in that respect.  Yet, as long as you are connected, everything is safe, and the chance of loss of data is down to a small number of keystrokes (and for when the browser/machine fails).&lt;/p&gt;
&lt;p&gt;By contrast the traditional install of Microsoft Excel means that it&amp;#8217;s going to be editing a file-system available &lt;span class="caps"&gt;XLS&lt;/span&gt; document.  While you&amp;#8217;re editing it, it keeps a backup version just in case it crashes for some reason.  You could safely reply on that backup, until you&amp;#8217;re ready to for a formal &lt;span class="caps"&gt;SAVE&lt;/span&gt; and close of the worksheet. Your alternate is to close without saving, which is the equivalent of abandoning the changes.  With either action, the backup file is deleted.  Excel&amp;#8217;s metaphor allows for working detached from the network which is handy in for &amp;#8220;on an airplane&amp;#8221; scenarios.&lt;/p&gt;
&lt;h3&gt;An &lt;span class="caps"&gt;SCM&lt;/span&gt;-like Workflow?&lt;/h3&gt;
&lt;p&gt;Is there a model where the web-server initiates a workflow for &amp;#8216;record&amp;#8217; of some sort, where work is ongoing on that record, and will be completed later?  Maybe the record is locked for the duration. maybe not. That would be &amp;#8216;work-in-progress&amp;#8217; and the representation of it should be mirrored like in GoogleDocs.  All changes to it would be safe. You also get the late &amp;#8220;submit&amp;#8221; or &amp;#8220;abandon&amp;#8221; reality of a detached model.  As it would be document-centric, there could well be a &amp;#8220;diffs&amp;#8221; that describe the modifications from a starting position, or from last advice.  That loses some of the intent of the changes being made though.  Martin has an article from &lt;a href="http://martinfowler.com/eaaDev/EventSourcing.html"&gt;seven years ago on Event Sourcing&lt;/a&gt; that is well worth a read. &lt;a href="http://martinfowler.com/eaaDev/EventNarrative.html"&gt;Event Narrative&lt;/a&gt; is a follow up.  Martin is much clearer on the chronalog of changes made to a model, during an implicit save/publish phase.  Would the micro-web-frameworks grow a synchronization side that can keep a backend informed with deltas every few seconds?  Right now you&amp;#8217;re on your own.  You write code to &lt;span class="caps"&gt;POST&lt;/span&gt; fragments to keep your models in sync, or you push whole documents.  There&amp;#8217;s no mirror/sync capability built into the current micro-web-frameworks.  The metaphor for the intended save or abandon, feels closer to the commit/merge concept from source control systems.&lt;/p&gt;
&lt;h3&gt;2/8/11 Update:&lt;/h3&gt;
&lt;p&gt;Alan Huffman, in email, says the name for what I&amp;#8217;m talking about here is &amp;#8220;Client-Side &lt;span class="caps"&gt;MVC&lt;/span&gt;&amp;#8221;.  Yep, that&amp;#8217;s it &amp;#8211; nice and short.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/YaA1vRV6IA4" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/06/webapp-model-quandary/</feedburner:origLink></item><item><title>Previous Web Architectures</title><link>http://feedproxy.google.com/~r/paulhammant/~3/aaI6TauKnuE/</link><pubDate>Mon, 06 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/06/previous-web-architectures</guid><description>&lt;p&gt;For enterprise app dev, and to support a following blog entry, I&amp;#8217;d like to recap technology choices previously used in the era of the web.&lt;/p&gt;
&lt;p&gt;What&amp;#8217;s the synopsis?  Over the years, logic is progressively migrating from a web-server tier into the browser.  Pattens were established in the web-server tier, and were starting to be defined in the web page.&lt;/p&gt;
&lt;h2&gt;Web application development in 1993, 2000 and 2006.&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/web_1993_2006.jpg" alt="" /&gt;&lt;/p&gt;
&lt;h3&gt;1993&lt;/h3&gt;
&lt;p&gt;Web applications were still tightly bound to the server hosting them.  Java, Python, &lt;span class="caps"&gt;PHP&lt;/span&gt; and Ruby were a few years from becoming realities for web development.  Perl perhaps was a possibility for web development, but you were most likely a C++ developer if you were making web-apps.  If Awk were viable then, I can&amp;#8217;t glean that from the wikipedia page for it.  JavaScript was a year away, and wouldn&amp;#8217;t be called JavaScript for another two.  If you submitted a form and hit the back button, you had little hope of anything useful happening.&lt;/p&gt;
&lt;p&gt;Beyond the declarative nature of &lt;span class="caps"&gt;HTML&lt;/span&gt; programming, and the understanding of the contracts behind URLs, there was no functionality in any turing-complete manner present in web pages.&lt;/p&gt;
&lt;h3&gt;2000&lt;/h3&gt;
&lt;p&gt;In the year 2000, Sun&amp;#8217;s Java Servlets or Java Server Pages (&lt;span class="caps"&gt;JSP&lt;/span&gt;) apps were a possibility with &lt;a href="http://en.wikipedia.org/wiki/Model-view-controller" title="MVC"&gt;Model-View-Controller&lt;/a&gt; being the recommended pattern. Microsoft had recovered from the mistake of ignoring the web in 1995, and had made Active Server Pages (&lt;span class="caps"&gt;ASP&lt;/span&gt;) the most popular enterprise solution. Indeed they had their fork of Java (google for &amp;#8220;Project &lt;span class="caps"&gt;COOL&lt;/span&gt;&amp;#8221;) .Net ready for enterprise app dev (see later).  &lt;span class="caps"&gt;PHP&lt;/span&gt; had come from nowhere as a &lt;a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface"&gt;&lt;span class="caps"&gt;CGI&lt;/span&gt;&lt;/a&gt; solution, and Apache with that mechanism of linkage was the most popular web server.  You might have even seen /cgi-bin/ in the URLs of web applications, though rewrites were possible.  Allaire&amp;#8217;s Cold Fusion (an independent &lt;span class="caps"&gt;IIS&lt;/span&gt; data-binding solution) had by then been snubbed when Microsoft didn&amp;#8217;t buy the 90% of the company they didn&amp;#8217;t already own and launched &lt;span class="caps"&gt;ASP&lt;/span&gt; instead. &lt;span class="caps"&gt;ASP&lt;/span&gt; apps followed the data-binding solution that fatter MS-Access solutions had pioneered for years.  &lt;a href="http://en.wikipedia.org/wiki/ASP.NET"&gt;&lt;span class="caps"&gt;ASP&lt;/span&gt;.Net&lt;/a&gt; was still two years away though.  Python and Ruby may have been used in &lt;span class="caps"&gt;CGI&lt;/span&gt; mode, but &lt;a href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt; (in 2003 I foolishly told &lt;span class="caps"&gt;DHH&lt;/span&gt; it wouldn&amp;#8217;t scale in the enterprise) was still three years away, and &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; two more.  Microsoft had pioneered &lt;span class="caps"&gt;AJAX&lt;/span&gt; style functionality in ActiveX controls, and you might have encountered applications that used them.  Similarly Sun could still make a claim that Java Applets were a reality on the web at large.  Macromedia&amp;#8217;s Shockwave/Flash was a viable platform that competed with, and would ultimately beat both ActiveX &amp;amp; Java Applets.&lt;/p&gt;
&lt;p&gt;Cookies are in every app by now (thanks to Netscape and Microsoft&amp;#8217;s subsequent adoption of them). JavaScript if used, was mostly for simpler validation concepts within pages, before allowing the &lt;span class="caps"&gt;POST&lt;/span&gt; to go through.&lt;/p&gt;
&lt;h3&gt;2006&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;AJAX&lt;/span&gt; was &lt;strong&gt;the&lt;/strong&gt; big thing.  Between them Microsoft, Netscape and Google had made it popular.  Most enterprise teams were building apps that were somewhere between Web 1.0 and Web 2.0. &lt;a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm"&gt;Roy Fielding&amp;#8217;s &lt;span class="caps"&gt;REST&lt;/span&gt;&lt;/a&gt; is big but it required some decoding for the layman. Ruby on Rails had been a significant productivity force for a year or two.  The .Net offerings for the web were solid 2.0 a year before and before the end of the year 3.0 would be released.  &lt;a href="http://en.wikipedia.org/wiki/Model-view-presenter" title="MVP"&gt;Model-View-Presenter&lt;/a&gt; was the paradigm, and Martin &lt;a href="http://www.martinfowler.com/eaaDev/uiArchs.html"&gt;talked about it&lt;/a&gt; after some initial ThoughtWorks projects with .Net. Microsoft&amp;#8217;s patterns and practices group would later &lt;a href="http://msdn.microsoft.com/en-us/library/cc304742.aspx"&gt;talk about it too&lt;/a&gt;.  That&amp;#8217;s not to say that &lt;span class="caps"&gt;MVC&lt;/span&gt; is dead &amp;#8211; most of the non-.Net frameworks are coded in accordance with it. All web-app best practitioners were now doing &lt;a href="http://en.wikipedia.org/wiki/Post/Redirect/Get" title="PRG"&gt;&lt;span class="caps"&gt;POST&lt;/span&gt;-redirect-&lt;span class="caps"&gt;GET&lt;/span&gt;&lt;/a&gt; and you finally hit the back button after posting a form &amp;#8211; take that &lt;a href="http://en.wikipedia.org/wiki/Tim_Berners-Lee"&gt;Tim Berners-Lee&lt;/a&gt;.  ThoughtWorks had a year or two before pushed out &lt;a href="http://seleniumhq.org/"&gt;Selenium&lt;/a&gt; for web-testing (and was one of the early pioneers of &lt;a href="http://en.wikipedia.org/wiki/Comet_(programming)"&gt;Comet&lt;/a&gt; as a result), then &lt;a href="http://sahi.co.in/"&gt;Sahi&lt;/a&gt; for another go at the same idea, with WebDriver (now Selenium2) to come a year later.  Web applications were now beautifully decomposed solutions, illustrating &lt;a href="http://en.wikipedia.org/wiki/Separation_of_concerns"&gt;Separation of Concerns&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Inversion_of_control"&gt;Inversion of Control&lt;/a&gt;, if not &lt;a href="http://martinfowler.com/articles/injection.html"&gt;Dependency Injection&lt;/a&gt; in particular.&lt;/p&gt;
&lt;p&gt;JavaScript usage is quite sophisticated now. Applications might issue &lt;span class="caps"&gt;AJAX&lt;/span&gt; operations to the back end to help with adaptive parts of the page, or individual controls.  Libraries exist to allow developers to economize and standardize on idioms.  These were &lt;a href="http://en.wikipedia.org/wiki/YUI_Library"&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt;&lt;/a&gt;, &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt;, &lt;a href="script.aculo.us"&gt;script.aculo.us&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Dojo_Toolkit"&gt;Dojo&lt;/a&gt;, and &lt;a href="http://mootools.net/"&gt;Mootools&lt;/a&gt;, but would see future &amp;#8220;winner&amp;#8221; of the genus JQuery arrive by the years end.  Early adopters would try out (in order of revolutionary zeal) &lt;a href="http://en.wikipedia.org/wiki/ExtJS"&gt;ExtJs&lt;/a&gt;, and &lt;a href="http://en.wikipedia.org/wiki/Google_Web_Toolkit"&gt;Google Web Toolkit&lt;/a&gt;, but the industry remained divided as to whether devs should embrace JavaScript or avoid it completely in the browser. &lt;a href="http://en.wikipedia.org/wiki/Cappuccino_(application_development_framework)"&gt;Cappuccino&lt;/a&gt; (even more revolutionary) would come a couple of years later, and suffer the same question.&lt;/p&gt;
&lt;p&gt;For the purposes of the following article, the take away is that Model-View-Controller in the web-server tier (as delivered by web frameworks) is a big thing in 2006.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/aaI6TauKnuE" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/06/previous-web-architectures/</feedburner:origLink></item><item><title>Angular and Selenium</title><link>http://feedproxy.google.com/~r/paulhammant/~3/HGoS6iRMEI8/</link><pubDate>Wed, 01 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/01/angular-and-selenium</guid><description>&lt;p&gt;I&amp;#8217;m the co-creator of the original Selenium 1.0 (or 0.something as it was then) but I mean Selenium2 (nee WebDriver) of course.  As UI&amp;#8217;s get more sophisticated, the ability to locate the right part of the page for purposes of a query or interaction, is increasingly hard.  I have personal experience with &lt;span class="caps"&gt;GWT&lt;/span&gt; that, on occasion, has driven me nuts and a pathological fear of the the super-advanced frameworks like &lt;a href="http://cappuccino.org/"&gt;Cappuccino&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finding Elements for and Angular app is not especially hard.  Indeed all of Backbone, Knockout, KnockBack, Spine, Ember, BatMan (and the rest) will paint relatively normal &lt;span class="caps"&gt;HTML&lt;/span&gt; and you could get busy with ID, &lt;span class="caps"&gt;CSS&lt;/span&gt; or &lt;span class="caps"&gt;XPATH&lt;/span&gt; based locators for widgets.  This blog entry could end there, but I&amp;#8217;d like to do a &amp;#8220;what if&amp;#8221;.&lt;/p&gt;
&lt;h2&gt;Model-Centric Angular Locators&lt;/h2&gt;
&lt;p&gt;Here is the model for &lt;a href="http://paulhammant.com/2012/01/29/(almost)-No-JavaScript-prototyping-with-Angular-in-a-single-source-file/"&gt;from monday&amp;#8217;s blog entry&lt;/a&gt; again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;authenticity_token&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;xotITlobeVi6BUnqBIqAd4eFy5+yJf0JlSyog8rK5hQ=&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index_files/grace-wireless-player.jpg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;grace-wireless-internet-radio&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Grace Wireless Internet Radio&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;179.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;imgSrc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index_files/plantronics-headphone.jpg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;plantronics-m40-mobile-in-ear-headset&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Plantronics M40 Mobile in Ear Headset&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here is part of the Angular page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;tbody&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;line_items&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item in cart.items&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{item.name}}&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{{item.imgSrc}}&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;h4&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://demo.spreecommerce.com/products/{{item.link}}&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{item.name}}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;unit-price&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{item.price | currency}} &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;operator&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; X &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;quantity&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width: 32px;&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item.qty&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;ng:validate=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;integer:1:5&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;operator&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; = &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;total&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{item.price*item.qty | currency}} &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;total&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cart.items.$remove(item)&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;delete button&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Remove&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;totals&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;6&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;totals&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Subtotal: {{cart.items.$sum(&amp;#39;price*qty&amp;#39;) | currency}} &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;What I would like to be able to do is locate parts of the page via the model that&amp;#8217;s presenting them.  Please excuse the Java:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;WebElement&lt;/span&gt; &lt;span class="n"&gt;quantityInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webDriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cart&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angularModel&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;items.$filter(&amp;#39;{ name: \&amp;quot;plantronics-m40-mobile-in-ear-headset\&amp;quot;}&amp;#39;).qty&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;quantityInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;15&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The initial findElement() is for the &lt;span class="caps"&gt;DIV&lt;/span&gt; that contains the &amp;#8216;MyController&amp;#8217; Angular reference, and is from the regular Selenium2 &lt;span class="caps"&gt;API&lt;/span&gt;.  The &amp;#8216;angularModel&amp;#8217; method is a Selenium2 locator yet to be  written for Angular interop.  The expression within is more or less handed over the wire to an Angular enabled Selenium2 controlled browser, and the lookup is against the model of course.  The Angular/Selenium integration piece returns the widget that is bound to that model node.  If there is no widget bound directly, then an exception is thrown all the way back to the test script.  If there is more than one widget bound directly to the model node, then a different exception is thrown.  Use of &amp;#8216;Quantity&amp;#8217; in formulae would be considered secondary.&lt;/p&gt;
&lt;p&gt;I wonder if $filter() as opposed to a more desirable $find().  The former will subset an array to all that match. What we want in this instance is a way to find an single matching element.&lt;/p&gt;
&lt;h2&gt;View-Centric Angular Locators&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s look for something less model-centric in the page, using a hypothetical Selenium/Angular locator that traverses view references.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;WebElement&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webDriver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cart&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angularView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item.price*item.qty&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equalTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$123.45&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Neat huh?  We could find anything Angular managed whether output or input.  Of course, Angular&amp;#8217;s recommended test idiom is via Misko&amp;#8217;s other stroke of genius &amp;#8211; &lt;a href="http://code.google.com/p/js-test-driver/"&gt;JsTestDriver&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Command Line: A Freebie&lt;/h2&gt;
&lt;p&gt;What if we had a custom control for Angular that allows an effective command line interaction with the current model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;.. snip ..
      &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;actions&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo()&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;continue&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Continue shopping&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;  
      or   &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo()&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;button checkout&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Checkout&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ng:commandline&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width: 500px; height: 100px;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
.. snip ..
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We could interact with that using the same model traversal techniques as described above.  ThoughtWorker colleagues would love for a command line interface to our &amp;#8220;time and expenses&amp;#8221; app.  It is currently a Rails app, and was previously a pre-Django Python app when it was the test bed for the earliest version of Selenium 1.0.  Before that it was a Lotus Notes app. Anyway, it could &lt;b&gt;easily&lt;/b&gt; be an Angular app with a command line for expedited time and/or expense entering:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/te_whatif.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In the race for dominance between &lt;a href="http://addyosmani.github.com/todomvc/"&gt;Backbone, Knockout, KnockBack, Spine, Ember, Batman, Sammy, YUILibrary, JavaScriptMVC, Broke, Fidel and my current favorite Angular&lt;/a&gt;, &lt;strong&gt;part&lt;/strong&gt; of the criteria for winning is going to include:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Elegant left-to-right style model traversal expressions.&lt;/li&gt;
	&lt;li&gt;Selenium2 integration for low effort full stack testing.&lt;/li&gt;
	&lt;li&gt;A command line interface :)&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/HGoS6iRMEI8" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/01/angular-and-selenium/</feedburner:origLink></item><item><title>Angular and DreamWeaver</title><link>http://feedproxy.google.com/~r/paulhammant/~3/-FWpqO2GkiA/</link><pubDate>Wed, 01 Feb 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/02/01/angular-and-dreamweaver</guid><description>&lt;p&gt;DreamWeaver feels like something from yesteryear, and maybe most web application development is done without it these days.  Flipping between a text editor like TextMate and a running browser like Firefox with Firebug and other plugins running is normal.&lt;/p&gt;
&lt;p&gt;That said, Dreamweaver has always claimed to be a &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt; editor for &lt;span class="caps"&gt;HTML&lt;/span&gt; source, that is aware of a number of markup regimes, with every intention of leaving stuff it does not recognize alone.  Stuff in this instance being elements and attributes principally, but also macros that it might see in between tags.  There were many years thought where DreamWeaver would be an admirable tool for round trip editing for &lt;span class="caps"&gt;ASP&lt;/span&gt;, but would desecrate a &lt;span class="caps"&gt;JSP&lt;/span&gt; page if it had the chance.  I&amp;#8217;m not sure if that&amp;#8217;s fixed in version 11.5, as who uses &lt;span class="caps"&gt;JSP&lt;/span&gt; anymore?&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the the excellent &lt;a href="http://spreecommerce.com"&gt;SpreeCommerce&lt;/a&gt; demo &lt;a href="http://paulhammant.com/2012/01/29/%28almost%29-No-JavaScript-prototyping-with-Angular-in-a-single-source-file/"&gt;from monday&amp;#8217;s blog entry&lt;/a&gt; cart again, this time in Dreamweaver.  Specifically the version of the original saved source, but after I used Dreamweaver to reformat the &lt;span class="caps"&gt;HTML&lt;/span&gt; source:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/spree_original.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Actually, the version before the reformat looks identical in DreamWeaver.&lt;/p&gt;
&lt;h2&gt;Viewing Angular source with DreamWeaver.&lt;/h2&gt;
&lt;p&gt;Here is what the same thing looks after I did the initial conversion to Angular:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/spree_deamweaver.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The Mustache style expressions into the &lt;span class="caps"&gt;JSON&lt;/span&gt; model are visible where they are inlined into text.  They are somewhere between unobtrusive and useful in this form.  Dreamweaver recognizes that I&amp;#8217;ve made changes outside the editor, and reloads.  If I&amp;#8217;m using either of &lt;a href="http://www.jetbrains.com/idea/"&gt;Intellij &lt;span class="caps"&gt;IDEA&lt;/span&gt;&lt;/a&gt; or &lt;a href="http://www.jetbrains.com/webstorm/"&gt;WebStorm&lt;/a&gt; they save on loss of focus automatically, so I&amp;#8217;m less likely to be in a state where I&amp;#8217;ve got unsaved changes in &lt;strong&gt;both&lt;/strong&gt;, and no ability to merge them.&lt;/p&gt;
&lt;h2&gt;Making a change in DreamWeaver&lt;/h2&gt;
&lt;p&gt;I changed some plain text and a mustache expression, and moved a column that I know to have an invisible Angular attribute in:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/spree_dw_edited.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;And here&amp;#8217;s how the source looked as DreamWeaver saved it.  Diff format for the file obviously, to show how utterly clean this was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="diff"&gt;&lt;span class="gh"&gt;diff --git a/index.html b/index.html&lt;/span&gt;
&lt;span class="gh"&gt;index e239f09..5d33561 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/index.html&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/index.html&lt;/span&gt;
&lt;span class="gu"&gt;@@ -51,7 +51,7 @@&lt;/span&gt;
     &amp;lt;nav&amp;gt;&amp;lt;a href=&amp;quot;http://demo.spreecommerce.com/products&amp;quot;&amp;gt;products&amp;lt;/a&amp;gt; &amp;lt;a href=&amp;quot;http://demo.spreecommerce.com/login&amp;quot; class=&amp;quot;cart&amp;quot;&amp;gt;Log In&amp;lt;/a&amp;gt; &amp;lt;/nav&amp;gt;
   &amp;lt;/header&amp;gt;
   &amp;lt;div id=&amp;quot;main&amp;quot; role=&amp;quot;main&amp;quot;&amp;gt;
&lt;span class="gd"&gt;-    &amp;lt;h1&amp;gt;Shopping Cart&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;lt;h1&amp;gt;CHANGED&amp;lt;/h1&amp;gt;&lt;/span&gt;
       &amp;lt;h3&amp;gt;You have {{cart.items.length}} items in your cart&amp;lt;/h3&amp;gt;
       &amp;lt;div&amp;gt;
         &amp;lt;table id=&amp;quot;cart-detail&amp;quot;&amp;gt;
&lt;span class="gu"&gt;@@ -60,25 +60,25 @@&lt;/span&gt;
               &amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Item&amp;lt;/th&amp;gt;
               &amp;lt;th&amp;gt;Price&amp;lt;/th&amp;gt;
               &amp;lt;th&amp;gt; &amp;lt;/th&amp;gt;
&lt;span class="gi"&gt;+              &amp;lt;th&amp;gt; &amp;lt;/th&amp;gt;&lt;/span&gt;
               &amp;lt;th&amp;gt;Qty&amp;lt;/th&amp;gt;
               &amp;lt;th&amp;gt; &amp;lt;/th&amp;gt;
               &amp;lt;th&amp;gt;Total&amp;lt;/th&amp;gt;
&lt;span class="gd"&gt;-              &amp;lt;th&amp;gt; &amp;lt;/th&amp;gt;&lt;/span&gt;
             &amp;lt;/tr&amp;gt;
           &amp;lt;/thead&amp;gt;
           &amp;lt;tbody id=&amp;quot;line_items&amp;quot;&amp;gt;
             &amp;lt;tr ng:repeat=&amp;quot;item in cart.items&amp;quot; class=&amp;quot;&amp;quot;&amp;gt;
               &amp;lt;td&amp;gt;&amp;lt;img alt=&amp;quot;{{item.name}}&amp;quot; ng:src=&amp;quot;{{item.imgSrc}}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;
&lt;span class="gd"&gt;-              &amp;lt;td class=&amp;quot;description&amp;quot;&amp;gt;&amp;lt;h4&amp;gt;&amp;lt;a href=&amp;quot;http://demo.spreecommerce.com/products/{{item.link}}&amp;quot;&amp;gt;{{item.name}}&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+              &amp;lt;td class=&amp;quot;description&amp;quot;&amp;gt;&amp;lt;h4&amp;gt;&amp;lt;a href=&amp;quot;http://demo.spreecommerce.com/products/{{item.link}}&amp;quot;&amp;gt;{{item.CHANGED}}&amp;lt;/a&amp;gt;&amp;lt;/h4&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
               &amp;lt;td class=&amp;quot;unit-price&amp;quot;&amp;gt; {{item.price | currency}} &amp;lt;/td&amp;gt;
               &amp;lt;td class=&amp;quot;operator&amp;quot;&amp;gt; X &amp;lt;/td&amp;gt;
&lt;span class="gi"&gt;+              &amp;lt;td class=&amp;quot;total&amp;quot;&amp;gt;&amp;lt;a ng:click=&amp;quot;cart.items.$remove(item)&amp;quot; class=&amp;quot;delete button&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
               &amp;lt;td class=&amp;quot;quantity&amp;quot;&amp;gt;&amp;lt;input style=&amp;quot;width: 32px;&amp;quot; name=&amp;quot;item.qty&amp;quot; ng:validate=&amp;quot;integer:1:5&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;
               &amp;lt;td class=&amp;quot;operator&amp;quot;&amp;gt; = &amp;lt;/td&amp;gt;
               &amp;lt;td class=&amp;quot;total&amp;quot;&amp;gt; {{item.price*item.qty | currency}} &amp;lt;/td&amp;gt;
&lt;span class="gd"&gt;-              &amp;lt;td class=&amp;quot;total&amp;quot;&amp;gt;&amp;lt;a ng:click=&amp;quot;cart.items.$remove(item)&amp;quot; class=&amp;quot;delete button&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
             &amp;lt;/tr&amp;gt;
           &amp;lt;tr class=&amp;quot;totals&amp;quot;&amp;gt;
&lt;span class="gd"&gt;-            &amp;lt;td colspan=&amp;quot;6&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+            &amp;lt;td colspan=&amp;quot;7&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;/span&gt;
             &amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;totals&amp;quot;&amp;gt; Subtotal: {{cart.items.$sum(&amp;#39;price*qty&amp;#39;) | currency}} &amp;lt;/td&amp;gt;
           &amp;lt;/tr&amp;gt;
             &amp;lt;/tbody&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There is one mistake concerning the column in moved leftwards: there is a total-row cell that spans 7 columns now instead of 6.  It&amp;#8217;s a shame that DreamWeaver does not have a &amp;#8220;move column&amp;#8221; refactoring.  When I said &amp;#8220;cut/paste&amp;#8221;, the reality was &amp;#8220;cut-column&amp;#8221;, &amp;#8220;insert blank column&amp;#8221; &amp;amp; select it, paste the clipboard into the blank column, then remove the now-blank rightmost column.  Removal was done in the source view as &amp;#8220;Delete Column&amp;#8221; does not actually delete the column, it just wipes out the content. That was the only operation not performed in &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt; mode.&lt;/p&gt;
&lt;p&gt;The hard part for DreamWeaver to do right here was the preservation of the ng:click=&amp;#8220;cart.items.$remove(item)&amp;#8221; attribute. It worked a treat, but this is not surprising as DreamWeaver has been preserving the extra attributes and elements for &lt;a href="http://en.wikipedia.org/wiki/ColdFusion_Markup_Language"&gt;Coldfusion Markup syntax&lt;/a&gt; from the mid-90&amp;#8217;s onwards.&lt;/p&gt;
&lt;p&gt;It seems to me that best practice with Angular if you&amp;#8217;re using DreamWeaver, is to re-format as needed in DreamWeaver, and not so in your preferred JetBrains &lt;span class="caps"&gt;IDE&lt;/span&gt;.  Then ping between the two tools at your leisure and feel smug at how clean the diffs are.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/-FWpqO2GkiA" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/02/01/angular-and-dreamweaver/</feedburner:origLink></item><item><title>(almost) No JavaScript prototyping with Angular in a single source file</title><link>http://feedproxy.google.com/~r/paulhammant/~3/RcOz_u3Z5Ck/</link><pubDate>Sun, 29 Jan 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/01/29/(almost)-No-JavaScript-prototyping-with-Angular-in-a-single-source-file</guid><description>&lt;p&gt;[ Following on from &lt;a href="/2012/01/23/ui-technology-paradigm-shift"&gt;last week&amp;#8217;s blog entry&lt;/a&gt; ] So what is this Angular stuff like in a real world app?  We&amp;#8217;ll I&amp;#8217;ve gone to the &lt;a href="http://demo.spreecommerce.com/"&gt;demo site&lt;/a&gt; for SpreeCommerce&amp;#8217;s &lt;a href="http://spreecommerce.com"&gt;open source shopping cart&lt;/a&gt; and saved a live snapshot of the page using the &lt;span class="caps"&gt;MAFF&lt;/span&gt; plugin for Firefox. SpreeCommerce is a good target for me presently, as the copyright/license for it does not preclude hacking to showcase Angular.&lt;/p&gt;
&lt;p&gt;I checked that into Github as a new repository : &lt;a href="https://github.com/paul-hammant/spreecommerce_angular"&gt;https://github.com/paul-hammant/spreecommerce_angular&lt;/a&gt;.  There are three commits, if you &lt;a href="https://github.com/paul-hammant/spreecommerce_angular/commits/master"&gt;look here&lt;/a&gt;.  The first is a checking as was saved.  The second, was the result of loading the page into DreamWeaver, and reformatting.  The third is the result of replacing the Web 1.0 functionality (Ruby on Rails) with Angular.  If you click on the &lt;a href="https://github.com/paul-hammant/spreecommerce_angular/commit/28d8cd8b54e005ca7624d2017bf49498ed16b731"&gt;diff link in the github interface&lt;/a&gt; you can see what I did precisely.  Generally though, I removed all other JavaScript, and changed the first row of the cart to a repeating row in the Angular style.  There&amp;#8217;s also a &lt;span class="caps"&gt;JSON&lt;/span&gt; model for the cart contents, which for the purposes of honoring this blog entry&amp;#8217;s title, I&amp;#8217;m going to consider that &lt;strong&gt;not JavaScript&lt;/strong&gt; as such.  The only real JavaScript is a wrapper for an an alert box, that&amp;#8217;s hooked into &amp;#8220;Continue Shopping&amp;#8221;, and &amp;#8220;Checkout&amp;#8221; as they don&amp;#8217;t actually work. The won&amp;#8217;t work until the backend is reworked to handle a &lt;span class="caps"&gt;JSON&lt;/span&gt; version of the cart contents on a &lt;span class="caps"&gt;POST&lt;/span&gt;.  Incidentally, if you&amp;#8217;re new to this, ng:repeat=&amp;#8220;item in cart.items&amp;#8221; is where the magic is really hooked in.&lt;/p&gt;
&lt;p&gt;You can play with the finished product here: &lt;a href="http://paul-hammant.github.com/spreecommerce_angular/"&gt;http://paul-hammant.github.com/spreecommerce_angular/&lt;/a&gt; (as well as look at the source in situ).  The page looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/spreecommerce_angular.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The difference to the original is that the &amp;#8220;update&amp;#8221; button is missing as &lt;span class="caps"&gt;POST&lt;/span&gt;-redirect-&lt;span class="caps"&gt;GET&lt;/span&gt; to the server side is not longer needed to recalculate the cart size.&lt;/p&gt;
&lt;h2&gt;Angular&amp;#8217;s advantage restated&lt;/h2&gt;
&lt;p&gt;The expressions that are possible on augmented arrays in particular, and angular &amp;#8216;functions&amp;#8217; generally, are the &lt;strong&gt;stroke of genius&lt;/strong&gt; power boost in my opinion.  They are allowing me to prototype without writing JavaScript functions, yet build a highly functional client-side application.  In the example above we have $sum(expression) used for a subtotal summing all the result of price times quantity for &lt;span class="caps"&gt;ALL&lt;/span&gt; the items in the &lt;strong&gt;model&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;colspan=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;totals&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Subtotal: {{cart.items.$sum(&amp;#39;price*qty&amp;#39;) | currency}} &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For the individual item &amp;#8216;row&amp;#8217; in the cart, $sum(..) is not used, but an expression is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;total&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; {{item.price*item.qty | currency}} &lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Misko has also added convenience functions to modify the model, for user interaction:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ng:click=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cart.items.$remove(item)&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;delete button&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Remove&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Not shown in this example, are &lt;a href="http://docs.angularjs.org/#!/api/angular.Array.filter" title="expression"&gt;$filter&lt;/a&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item in cart.items.$filter(&amp;#39;price &amp;gt; 9.95&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;..&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Nor is &lt;a href="http://docs.angularjs.org/#!/api/angular.Array.orderBy" title="field, reverseOrNot"&gt;$orderBy&lt;/a&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item in cart.items.$orderBy(&amp;#39;price&amp;#39;, false)&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;..&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Expressions can be hard coded in single-quotes, or variables passed in.  In the case of ordering tables, you&amp;#8217;d assign the order column to a variable, and have some other UI control change that (as well as ascending or descending).  I&amp;#8217;ve an example of that, and filtering here from a year ago: &lt;a href="http://paul-hammant.github.com/StoryNavigator"&gt;http://paul-hammant.github.com/StoryNavigator&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Powerful stuff huh?&lt;/p&gt;
&lt;h2&gt;A better Etsy.com example.&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://etsy.com"&gt;Etsy.com&lt;/a&gt; (a fantastic shopping site for hand-made designer craft items) have a items within stores within the cart.  It&amp;#8217;s more of a model.  I&amp;#8217;ve made an Angular version of their cart too, but presently don&amp;#8217;t &lt;strong&gt;yet&lt;/strong&gt; have permission to push the source to Github (it&amp;#8217;s copyrighted to them, not open source).  Here&amp;#8217;s the screen-shot:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/etsy_angular.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;JSON&lt;/span&gt; source for the &amp;#8216;cart&amp;#8217; looks like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;cartId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;138229652&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;GoldenSection&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;89166902&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;January - photography print calendar zodiac Aquarius Capricorn winter birthday owl romantic gift for her him unisex home decor wall art love&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="kr"&gt;short&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;january-photography-print-calendar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;shipping&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index_files/il_75x75.297883532.jpg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nx"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Paypal&amp;quot;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;cartId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;138148701&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;RetroModernArt&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;69841278&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Bike Art Black Tandum Bike 8x10 Linocut art print&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="kr"&gt;short&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bike-art-black-tandum-bike-8x10-linocut&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;shipping&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index_files/il_75x75.249358938.jpg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;85933033&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Valentines Day LOVE Red Block Print - LOVE art in RED Typography Woodblock 5 x 7 on white paper&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="kr"&gt;short&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;valentines-day-love-red-block-print-love&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;shipping&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;index_files/il_75x75.297883532.jpg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nx"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Paypal&amp;quot;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are some interesting things to note, that are not as prominent in the SpreeCommerce cart that I have released the source for, but still true:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;I&amp;#8217;m mixing reference information in the cart&amp;#8217;s &lt;span class="caps"&gt;JSON&lt;/span&gt;, alongside mutable entities like &amp;#8216;quantity&amp;#8217;.&lt;/li&gt;
	&lt;li&gt;index_files the directory does not really hold any images, that&amp;#8217;s just the way the &lt;span class="caps"&gt;MAFF&lt;/span&gt; plugin saved a local copy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the case of the former, I could do a refactoring to separate the &amp;#8216;never changes&amp;#8217; reference data from the cart contents that does change (quantities change and lines disappear). In the end though it does not matter. The whole &lt;span class="caps"&gt;JSON&lt;/span&gt; document being pushed back to the server for a &amp;#8216;checkout&amp;#8217; action is hardly going to hurt it.  I&amp;#8217;ve optimized the &lt;span class="caps"&gt;JSON&lt;/span&gt; document to support the user interface composition, which is what I think you &lt;strong&gt;should do&lt;/strong&gt; for Angular apps.&lt;/p&gt;
&lt;h3&gt;Etsy&amp;#8217;s Cart Size.&lt;/h3&gt;
&lt;p&gt;Remember that items are within stores that are within the cart.  Here&amp;#8217;s the use of $sum() to flatten that to a single number:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;items.length&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Etsy&amp;#8217;s orders have a shipping amount too.&lt;/h3&gt;
&lt;p&gt;The per store expression to calculate a total for the items obeys the &lt;a href="http://en.wikipedia.org/wiki/Order_of_operations"&gt;standard order of operations&lt;/a&gt; of course:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;price*qty + shipping&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Etsy&amp;#8217;s grand total:&lt;/h3&gt;
&lt;p&gt;Well this isn&amp;#8217;t possible yet, in an expression. Just as well its not needed for the Etsy cart:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;store.$sum(&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;qty&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;shipping&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Yeesh!&lt;/p&gt;
&lt;h2&gt;More Aggregate Functions&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m really enamored by &lt;strong&gt;thinking&lt;/strong&gt; of the &lt;span class="caps"&gt;JSON&lt;/span&gt; document as a live database within the web page.  I was pretty good at &lt;span class="caps"&gt;SQL&lt;/span&gt; in the early 90&amp;#8217;s and loved the composition of reports with judicious use of SQL&amp;#8217;s &lt;span class="caps"&gt;GROUP&lt;/span&gt; BY, &lt;span class="caps"&gt;HAVING&lt;/span&gt; and &lt;span class="caps"&gt;UNION&lt;/span&gt;.  If you ever meet me for an interview (ThoughtWorks or Client) and have put &lt;span class="caps"&gt;SQL&lt;/span&gt; on your resume, I&amp;#8217;m going to see how far you can go with those.&lt;/p&gt;
&lt;p&gt;Take a look at a &lt;a href="http://www.w3schools.com/sql/sql_groupby.asp"&gt;tutorial for Group-By&lt;/a&gt;. We can used that to explore a few more array (aggregate) functions in Angular. Functions that don&amp;#8217;t exist yet, but I hope will be implemented in due course.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/sql_table_data.png" alt="" /&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;OrderDate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2008/11/12&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;OrderPrice&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hansen&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="nx"&gt;etc&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Group-by:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Customer&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Total Order&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;custOrd in orders.$groupBy(&amp;#39;Customer&amp;#39;).$select(&amp;#39;Customer, sum(OrderPrice) as OrderTotal&amp;#39;)&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.Customer}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.OrderTotal}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Having (equivalent):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Customer&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Total Order&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;custOrd in orders.$groupBy(&amp;#39;Customer&amp;#39;).$select(&amp;#39;Customer, sum(OrderPrice) as OrderTotal&amp;#39;).$filter(&amp;#39;OrderTotal &amp;gt; 1000&amp;#39;)&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.Customer}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.OrderTotal}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Min, Max, Average:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Customer&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Minimum Order&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Maximum Order&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;Average Order&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;ng:repeat=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;custOrd in orders.$groupBy(&amp;#39;Customer&amp;#39;).$select(&amp;#39;Customer, min(OrderPrice) as MinOrder, max(OrderPrice) as MaxOrder, mean(OrderPrice) as AverageOrder&amp;#39;)&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.Customer}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.MinOrder}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.MaxOrder}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{custOrd.AverageOrder}}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Simpler aggregate Functions.&lt;/h2&gt;
&lt;p&gt;It is possible to consider the following &amp;#8230;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;orders.$min(&amp;#8216;OrderPrice&amp;#8217;)&lt;/li&gt;
	&lt;li&gt;orders.$max(&amp;#8216;OrderPrice&amp;#8217;)&lt;/li&gt;
	&lt;li&gt;orders.$mean(&amp;#8216;OrderPrice&amp;#8217;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#8230; on the whole array, &lt;strong&gt;or&lt;/strong&gt; an array after being filtered:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Hansen average: {{ orders.$filter(&amp;quot;{ Customer: &amp;#39;Hansen&amp;#39;}&amp;quot;).$mean(&amp;#39;OrderPrice&amp;#39;) }} &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It is much less useful than the pivoted table version that Group-By gives us though.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/RcOz_u3Z5Ck" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/01/29/%28almost%29-No-JavaScript-prototyping-with-Angular-in-a-single-source-file/</feedburner:origLink></item><item><title>UI Technology Paradigm Shift</title><link>http://feedproxy.google.com/~r/paulhammant/~3/ngwVLW20aTs/</link><pubDate>Mon, 23 Jan 2012 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2012/01/23/ui-technology-paradigm-shift</guid><description>&lt;p&gt;I&amp;#8217;ve blogged about &lt;a href="http://angularjs.org"&gt;Angular&lt;/a&gt; &lt;a href="http://paulhammant.com/blog/angular-declarative-ui.html"&gt;before in 2009&lt;/a&gt;, and I&amp;#8217;ve been aware of it since May 29th, 2009 when &lt;a href="http://olabini.com/blog/"&gt;Ola Bini&lt;/a&gt; and had breakfast with &lt;a href="http://misko.hevery.com/"&gt;Misko Hevery&lt;/a&gt; at Google before rolling into the first &amp;#8220;Wave hackathon&amp;#8221; the day after it was announced at Google I/O. We (I ?) chuckled at Misko then for his in-a-web-page idea, and since finding out more, I&amp;#8217;m regretting that.&lt;/p&gt;
&lt;h2&gt;What has changed?&lt;/h2&gt;
&lt;p&gt;Well it has been over two and a half years since Misko and Adam Abrons started this thing. Initially they were thinking of a data-service priced by the mega-byte and you&amp;#8217;d host your &lt;a href="http://angularjs.org"&gt;Angular&lt;/a&gt; pages anywhere. Now with Adam not so active for the time being, and Googlers Igor Minar, Vojta Jina as well as many contributors, Misko&amp;#8217;s &lt;a href="http://angularjs.org"&gt;Angular&lt;/a&gt; is more than ready for large app development. Production ready, despite not being a 1.0 release yet (that&amp;#8217;s coming soon I&amp;#8217;m told).&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s also new competition in the &amp;#8220;declarative &lt;span class="caps"&gt;HTML&lt;/span&gt; extending tech&amp;#8221; space &amp;#8211; &lt;a href="http://documentcloud.github.com/backbone"&gt;Backbone&lt;/a&gt; (October 2010), &lt;a href="http://knockoutjs.com"&gt;Knockout&lt;/a&gt; (July 2010), &lt;a href="http://batmanjs.org"&gt;Batman&lt;/a&gt; (January 2011), &lt;a href="http://kmalakoff.github.com/knockback"&gt;Knockback&lt;/a&gt; (November 2011), &lt;a href="http://developer.yahoo.com/yui/"&gt;&lt;span class="caps"&gt;YUI&lt;/span&gt; Library&lt;/a&gt; (urggh; when??).&lt;/p&gt;
&lt;p&gt;Knockback is a serious attempt to merge the best of Backbone and Knockout by Kevin Malakoff, that deserves further attention I think. If nothing else, it&amp;#8217;s comparison table is a powerful starting point to think about the pros and cons of these technologies.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s the criteria for selection?&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Can &lt;strong&gt;bind&lt;/strong&gt; to nodes in a &lt;span class="caps"&gt;JSON&lt;/span&gt; document, especially for repeating ones.&lt;/li&gt;
	&lt;li&gt;Feels like an extension to &lt;span class="caps"&gt;HTML&lt;/span&gt; (albeit not a W3C approved one)&lt;/li&gt;
	&lt;li&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; is round-trip editable in DreamWeaver. e.g. DreamWeaver does &lt;strong&gt;not&lt;/strong&gt; take out &lt;span class="caps"&gt;HTML&lt;/span&gt; attributes it does not recognize on &amp;#8216;Save&amp;#8217; so technologies that rely on attributes for implementation are safe.&lt;/li&gt;
	&lt;li&gt;Doesn&amp;#8217;t require much Javascript/CoffeeScript in order to get into the rapid prototyping of the whole UI experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Not making the cut as far as I&amp;#8217;m concerned.&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://javascriptmvc.com/"&gt;JavaScriptMVC&lt;/a&gt; doesn&amp;#8217;t make the list because of its &lt;a href="https://github.com/jupiterjs/todo/blob/master/todo/index.html"&gt;verbose templating / binding&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://emberjs.com"&gt;Ember&lt;/a&gt; (some time in early 2011?) sadly &lt;a href="https://github.com/addyosmani/todomvc/blob/master/architecture-examples/emberjs/index.html"&gt;still has a verbose &lt;span class="caps"&gt;HTML&lt;/span&gt; syntax&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://spinejs.com"&gt;Spine&lt;/a&gt; (when?) has some ugliness around its   logic that makes it feel like it&amp;#8217;s not an natural extension of &lt;span class="caps"&gt;HTML&lt;/span&gt;.&lt;br /&gt;
&lt;a href="http://sammyjs.org"&gt;Sammy&lt;/a&gt; (March 2009) has &lt;a href="https://github.com/addyosmani/todomvc/blob/master/architecture-examples/sammyjs/templates/todolist.template"&gt;separated templates that feel like a whole other world&lt;/a&gt;. This is only a little further than backbone which I like a little, so maybe I&amp;#8217;m being too hard on Sammy.&lt;/p&gt;
&lt;p&gt;Not sure about my selections? &amp;#8211; take a look for yourself: Addy Osmani has a repo that compares implementations of a &lt;span class="caps"&gt;TODO&lt;/span&gt; list &lt;a href="http://addyosmani.github.com/todomvc"&gt;on Github&lt;/a&gt;  That project is the definitive place for comparing these presently. That said the reader some take some caution, as some of the &lt;span class="caps"&gt;TODO&lt;/span&gt; list implementations are minimalist, and some have &amp;#8216;bells and whistles&amp;#8217;.&lt;/p&gt;
&lt;h2&gt;The Paradigm shift.&lt;/h2&gt;
&lt;p&gt;If you get into the way of working with these technologies, you start to think of &lt;strong&gt;the document on the wire as the contract&lt;/strong&gt;. That document is &lt;span class="caps"&gt;JSON&lt;/span&gt; of course, and emerged through iterations of UI development. The development is so rapid that you could almost sit next to your end user and ever minute or so say &amp;#8220;how about this?&amp;#8221;&lt;/p&gt;
&lt;p&gt;This is Model View Controller (&lt;span class="caps"&gt;MVC&lt;/span&gt;) in a web page at its best. Purists will say that it&amp;#8217;s Model View View Model (&lt;span class="caps"&gt;MVVM&lt;/span&gt;) which is a derivation of Model View Presenter, which as far as I&amp;#8217;m concerned was necessary because of the unit-testing complexity of earlier &lt;span class="caps"&gt;ASP&lt;/span&gt;.Net implementations. I&amp;#8217;m really happy with thinking of this as &lt;span class="caps"&gt;MVC&lt;/span&gt; though.&lt;/p&gt;
&lt;p&gt;It will force you to change your back-end from traditional designs. It is going to have to serve up that document for the page load, and accept it back (modified) and subject to verification when the end-user finishes with the page. Maybe incrementally as the page is used are a possibility.  As such the storage for the data behind the document could potentially be a lead weight. If you choose a traditional design (highly normalized data), that&amp;#8217;s going to definately be the case. Perhaps if you are hell bent on that reality, you should delay its creation as long as possible while developing the app, and use something &lt;a href="http://martinfowler.com/bliki/NosqlDefinition.html"&gt;lightweight for the time being&lt;/a&gt;. In terms of backend technologies you&amp;#8217;re still wide open, though ones that implement &lt;span class="caps"&gt;JSON&lt;/span&gt; are a must. Node, Scala, Ruby, Groovy have &lt;span class="caps"&gt;JSON&lt;/span&gt; built in, though even Java can play well too with the likes of &lt;a href="http://code.google.com/p/google-gson/"&gt;&lt;span class="caps"&gt;GSON&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The whole development experience is going to be faster. Perhaps 10x over what you&amp;#8217;re used to. I&amp;#8217;m reminded of Microsoft-Access applications from the mid 90&amp;#8217;s where non-technical people could put together shared DB apps for small teams with forms/ reports etc quite quickly. Lotus Notes plays (played) to the same space. Cold Fusion and &lt;span class="caps"&gt;PHP&lt;/span&gt;, back then, were attempts to get the simplicity into web app development, and to lower the bar for development generally. Anyway, the data-bound yet &lt;span class="caps"&gt;MVC&lt;/span&gt; nature of &lt;a href="http://angularjs.org"&gt;Angular&lt;/a&gt; and its ilk really are an accelerator on rapid app dev. I&amp;#8217;ve also been measuring page performance with the latest release across for all tier one browsers, and some four thousand data-bound fields on an local Angular page are now being rendered inside of a second. As a result, I&amp;#8217;m happy this is quick enough for most apps.&lt;/p&gt;
&lt;p&gt;This stuff is so fast for application development, that it should really be your default for new projects, and you then determine &lt;strong&gt;if&lt;/strong&gt; you need to shift away from it for more sophisticated applications. To give you a feel to suitability &amp;#8211; booking apps, shopping experiences, and anything that would have fitted a 100 table schema are all very much within reach of Angular, Knockback etc. Content Management systems, Games, GoogleDocs, apps that &lt;a href="http://280slides.com/Editor/"&gt;feel like desktop ones&lt;/a&gt; are perhaps not. For those think of &lt;a href="http://code.google.com/webtoolkit/"&gt;&lt;span class="caps"&gt;GWT&lt;/span&gt;&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Ext_JS"&gt;ExtJS&lt;/a&gt; and &lt;a href="http://cappuccino.org/"&gt;Cappuccino&lt;/a&gt; if they must be web-apps.&lt;/p&gt;
&lt;p&gt;If these technologies take off, Agile practitioners should be pleased because it shifts the emphasis away from data driven / long-cycle design towards an naturally iterative and emergent one. UX driven development/design, in order words.&lt;/p&gt;
&lt;h2&gt;Thoughts on Angular per se.&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s the leader as far as I&amp;#8217;m concerned because of the ability to drive a single page from model mutations and Angular attributes sprinkled throughout the page.  Even occasional or modal parts of the page.  The breakthrough thinking for me was a realization that you could have more than one model in a page.  One for the data that came from the server, and one for the flow-control for the page that isn&amp;#8217;t going to be saved anywhere.  One more for the reference data that came from the server (or was inlined in the page, if it doesn&amp;#8217;t change too much).  More than once I&amp;#8217;ve saved a &lt;span class="caps"&gt;HTML&lt;/span&gt; page using Firefox&amp;#8217;s &lt;span class="caps"&gt;MAFF&lt;/span&gt; plugin, then operated on the source to a) remove all JavaScript that normally makes the page, replace the &lt;span class="caps"&gt;JSON&lt;/span&gt; and models that the original web-technology placed in the page with an Angular loaded &lt;span class="caps"&gt;JSON&lt;/span&gt; document.  You could do so quite easily for a Amazon&amp;#8217;s cart page, or any Airline&amp;#8217;s &amp;#8220;available flights&amp;#8221; page. With Angular in particular, I&amp;#8217;m not having to push &lt;span class="caps"&gt;HTML&lt;/span&gt; into &amp;#8216;script&amp;#8217; tags to make them into templates.&lt;/p&gt;
&lt;p&gt;Angular sells itself via the tag line &amp;#8220;What &lt;span class="caps"&gt;HTML&lt;/span&gt; would have been, had it been designed for web apps&amp;#8221; which is better than their page title &amp;#8220;Superheroic JavaScript &lt;span class="caps"&gt;MVC&lt;/span&gt; Framework&amp;#8221;. Decipher Inc suggest &lt;a href="http://www.decipherinc.com/n/blog/development-and-engineering-team/2011/04/angular-model-single-source-truth"&gt;The model is the single source of truth&lt;/a&gt;.  I think there&amp;#8217;s some general messaging that could be improved, especially when compared to the competing frameworks.  It is also lamentable that the AngularJS.org site is (a) not presently indexed by Google, and (b) a place to get stuck on, as the back-button doesn&amp;#8217;t allow you to leave it.&lt;/p&gt;
&lt;p&gt;Lastly, I think they&amp;#8217;re headed in the wrong direction on one specific aspect.  Array functions (as they call them), that would have lead into more sophisticated aggregate functions in the style of SQL&amp;#8217;s Group-By and Having.  In their live example &lt;a href="http://angularjs.org"&gt;on the lower right of their front page&lt;/a&gt;, the remaining number of TODOs is presently:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{remaining()}} remaining&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Mapping to a JavaScript function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This used to be the following expression, without an accompanying JavaScript function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{todos.$count(&amp;quot;!done&amp;quot;)}} remaining&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I hope they&amp;#8217;ll revert the commit that took that out.&lt;/p&gt;
&lt;p&gt;Next up : &amp;#8220;(almost) No JavaScript prototyping with Angular in a single source file&amp;#8221;&lt;/p&gt;
&lt;p&gt;PS &amp;#8211; I&amp;#8217;m still dreaming of a nested UI markup that&amp;#8217;s not an derivative of &lt;span class="caps"&gt;XML&lt;/span&gt; that has inline turing completeness and closures.&lt;/p&gt;
&lt;p&gt;Jan 28 &amp;#8211; update&lt;/p&gt;
&lt;p&gt;Kevin has a thorough &lt;a href="http://braincode.tumblr.com/post/16351440663/tear-down-these-walls"&gt;response on his blog&lt;/a&gt;.  Well worth a read!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/ngwVLW20aTs" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2012/01/23/ui-technology-paradigm-shift/</feedburner:origLink></item><item><title>Sauce Labs: Tunnels or not?</title><link>http://feedproxy.google.com/~r/paulhammant/~3/mci0HeDBaoM/</link><pubDate>Sun, 11 Dec 2011 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/12/11/sauce-labs-tunnels-or-not</guid><description>&lt;p&gt;Below are three architecture diagrams pertaining to Sauce Labs &lt;a href="http://saucelabs.com/ondemand"&gt;OnDemand&lt;/a&gt; usage for parallel testing of webapps with Selenium 1 or 2.  You&amp;#8217;re using Sauce Labs, of course, if you need 50 to 500 parallel browsers testing a web-app constructed from one or more developer commits &amp;#8211; right ?&lt;/p&gt;
&lt;h2&gt;Using a Sauce Connect Tunnel for content&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/SauceLabs_Tunnel.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;An illustration of Sauce Labs&amp;#8217; &lt;a href="http://saucelabs.com/docs/sauce-connect"&gt;Sauce Connect&lt;/a&gt; technology being used to poke through your corporate firewall to the web-server that otherwise is not accepting connections from &amp;#8216;outside&amp;#8217;&lt;/p&gt;
&lt;h2&gt;Using a Sauce Connect Tunnel for content and commands&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/SauceLabs_Tunnel2.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;As above but the commands from Selenium go the browser also go through the tunnel.  While the normal way sends commands over port 80 and/or 443, and your firewall will see them as normal traffic, there could be benefits from marshaling all traffic through on connection (as shown). Sauce&amp;#8217;s tunnel doesn&amp;#8217;t use &amp;#8216;Beep&amp;#8217; but if you don&amp;#8217;t understand the concepts, a &lt;a href="http://www.ibm.com/developerworks/webservices/library/x-beep/"&gt;quick read&lt;/a&gt; will be helpful.&lt;/p&gt;
&lt;h2&gt;Publishing your test app directly on the web.&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/SauceLabs_No_Tunnel.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;If you deploy the web on a public sub-domain, OnDemand can test it without a tunnel.  This is preferable of course, as it will be faster.  Most likely you are choosing temporary domain names like 904f628c-23ad-40e6-83f8-48600d242e36.example.com and discarding that as soon as the test suite is complete.  There&amp;#8217;s some tooling complexity there as you&amp;#8217;re doing to have make your &lt;span class="caps"&gt;DNS&lt;/span&gt; dynamic, but nobody said you had to use your main corporate domain.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/mci0HeDBaoM" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/12/11/sauce-labs-tunnels-or-not/</feedburner:origLink></item><item><title>Cookie Cutter Scaling</title><link>http://feedproxy.google.com/~r/paulhammant/~3/C_cU5WCptcU/</link><pubDate>Tue, 29 Nov 2011 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/11/29/cookie-cutter-scaling</guid><description>&lt;p&gt;It is true that the industry generally no longer does vertical scaling &amp;#8211; buying bigger and bigger boxes to host single deployments of nodes in a stack.  For many years, horizontal scaling has been the normal scaling technique. There is some variation in the implementations of that idea though. One particular style, where all horizontally scaled nodes are identical (which I&amp;#8217;ll call cookie cutter for the sake of this posting) pays dividends in terms of easy deployment and the ability to simply add boxes to add capacity to the stack.&lt;/p&gt;
&lt;h2&gt;Deliberate Multi-Tier Designs&lt;/h2&gt;
&lt;p&gt;One common one for enterprises is to have a multi-tier architecture with &lt;span class="caps"&gt;TCP&lt;/span&gt;/IP separating each tier, and each horizontally scaled according to &lt;span class="caps"&gt;CPU&lt;/span&gt; load:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/CookieCutterScaling1.jpg" title="Horizontal stack with CPU load emphasis" alt="Horizontal stack with CPU load emphasis" /&gt;&lt;/p&gt;
&lt;p&gt;Explanation: In the above, the observed (or estimated) &lt;span class="caps"&gt;CPU&lt;/span&gt; loads of the production environment drive the numbers of each node at each tier.  Assuming identical hardware for each process, it is easy to predict how many and where new hardware has to be added for a horizontal increase in capacity.  In the diagram above, to denote an intended capacity increase, I have two ghosted nodes at the W (Web) tier, and one ghosted node for each of the P and Q nodes (whatever they are).  In our diagram we model two ultimate backends.  They could be an big-bucks Oracle DB, &lt;span class="caps"&gt;ERP&lt;/span&gt; or legacy &lt;span class="caps"&gt;IBM&lt;/span&gt; mainframe.  They can add their own constraints like a max number of concurrent connections, that would affect everything I&amp;#8217;m trying to sell here.&lt;/p&gt;
&lt;h3&gt;Scaling problem&lt;/h3&gt;
&lt;p&gt;When the production load on the stack is getting close to the maximums or starting to degrade capacity, then &lt;span class="caps"&gt;CPU&lt;/span&gt; percentage loads can be used as a way to work out &lt;strong&gt;which&lt;/strong&gt; tier needs a new node to alleviate load (the shaded boxes in the diagram).  That, or some more complex analysis according to &lt;a href="http://en.wikipedia.org/wiki/Amdahl&amp;#39;s_law"&gt;Amdahl&amp;#8217;s law&lt;/a&gt;. With each deployment in one tier as opposed to another, there are configuration considerations.  Lastly, your architecture (and its configuration) might be able to suffer nodes being provisioned while the stack is up, or it might require being taken down totally (like the &amp;#8216;Apple store&amp;#8217; today, though that might be because of mystique).&lt;/p&gt;
&lt;h2&gt;Cookie Cut Designs Recommended&lt;/h2&gt;
&lt;p&gt;The premise is that you compress up as many tiers into the top tier as possible.  Optimally into the same process, so you&amp;#8217;re avoiding between-machine &lt;span class="caps"&gt;TCP&lt;/span&gt;/IP if you can. This assumes that you are using the same base technology in a relatively homogenous solution.  Corporate Java or .Net teams are used to that reality.  Quite often startups are not, as they&amp;#8217;ll use any technology that&amp;#8217;s reasonable to get functionality live quickly. If you cannot get everything inside one process (one Java or .Net VM), then a localhost &lt;span class="caps"&gt;TCP&lt;/span&gt;/IP interface will suffice.&lt;/p&gt;
&lt;p&gt;Anyway, here&amp;#8217;s a diagrammatic representation of the cookie cutter version of the previous architecture.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/CookieCutterScaling2.jpg" title="Horizontal stack with cookie cutter emphasis" alt="Horizontal stack with cookie cutter emphasis" /&gt;&lt;/p&gt;
&lt;p&gt;Here we have W, P and Q in in one process.  The designers should have drunk the &lt;a href="http://martinfowler.com/articles/injection.html"&gt;Inversion of Control and Dependency Injection&lt;/a&gt; kool-aid to allow a sane structuring of the components of this application, and to minimize the side effects of running multiple things concurrently in one virtual machine.  We&amp;#8217;ve eliminated some but not all of the downstream calls. With this design we still have to worry about timeouts and caching to a different degree.  Other things will become problems that were not before.  Specifically we have more classes in one VM (&lt;span class="caps"&gt;JVM&lt;/span&gt; or &lt;span class="caps"&gt;CLR&lt;/span&gt; for .Net) or now, and should worry about getting the memory allocations correctly.  Then there is the sidestepping all the &lt;span class="caps"&gt;TCP&lt;/span&gt;/IP formerly involved, as well as the implicit error handling or failover for outage.  We should also think about whether there is garbage collection that might have been workable before, but has tipped over a threshold since putting more in one &lt;span class="caps"&gt;JVM&lt;/span&gt;/&lt;span class="caps"&gt;CLR&lt;/span&gt;.  Perhaps also, there is dynamic class loading and implicit unloading that could cause the issues &lt;a href="http://boxysystems.com/index.php/tomcat-v5-0-28-memory-leak-exposed/"&gt;like they have for others in the past&lt;/a&gt;. The downside could also include reaching the constrains of the big or legacy back-end systems.  The mainframe may only be able to support a fixed number of concurrent connections.&lt;/p&gt;
&lt;p&gt;In some senses, this is moving &amp;#8216;functionality via remote services&amp;#8217;, to &amp;#8216;functionality in an embeddable library&amp;#8217;.  Whereas services can have their own release schedule, and the larger stack need not be deployed in lock-step, embeddable libraries are different.  Specifically, if there is a defect in the embeddable library version of the same thing, how quickly can all systems that use it be redeployed.  Is your deployment organization adept enough at rolling out upgrades, or are you 18 months at a time with the same release?&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;p&gt;We could consider that verifying Zipcodes or calculating Geo-ip data are candidates for external services, with elegant RESTful interfaces.  There would be a team around that of course, and it would get its own release schedule.  That would be a traditional enterprise solution right?  A sizing exercise would work out how many nodes were required in production, and the release engineers would get busy adding domain names and port numbers to configuration.  With the cookie cutter alternative, those two would be deployed in the same process as the things that would use them.  You would do some sums to determine whether the added data for each were was worth the cost.&lt;/p&gt;
&lt;p&gt;Actually Zipcodes and Geo-IP data are edge-case use in a typical stack.  I blogged before about &lt;a href="http://paulhammant.com/2011/11/22/google-accounts-architectural-meaning/"&gt;Google Accounts&lt;/a&gt;, and maybe Google would do Geo-ip via a redirect on a missing or out of date cookie.  At least they are tangible examples though.&lt;/p&gt;
&lt;h2&gt;Deploying A Cookie Cutter Solution&lt;/h2&gt;
&lt;p&gt;From the deployer&amp;#8217;s point of view, there are far fewer nodes types to think about.  If the stack has load-balancing (say F5) in front of the web tier, then registering new nodes with that is the only configuration &amp;#8216;chore&amp;#8217;.   In terms of deployment at a pertinent go-live moment, and assuming humans are still involved (versus &lt;a href="http://continuousdelivery.com"&gt;Continuous Delivery&lt;/a&gt;), a slider metaphor may be appropriate:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/CookieCutterScaling3.jpg" title="Hypothetical Deployment UI" alt="Hypothetical Deployment UI" /&gt;&lt;/p&gt;
&lt;p&gt;Edging the slider to the right is a safer than an all or nothing flipping of a switch for a release.  Using the measured incidence of exceptions from the logs is a good way of gauging the success of the upgrade while 10% of the way in.  Of course whether your app is stateful or stateless has a bearing on whether this will work or not.  If it is stateful, F5 is going to try to keep people attached to instances hosting the old version.  You&amp;#8217;re going to have to log session out periodically if their session is inactive, in order to free up nodes.  If you use Gmail, you notice that Google logs you our periodically.  Sometimes sooner than you&amp;#8217;d expect, or two separate gmail accounts get logged out at the same time, and that could be correlated with a upgrade progression like this.  If the stack is stateless, then moving folks from one node to another mid-session is far easier. Although, that would only work for releases where there a few discernible differences between the old and the new.&lt;/p&gt;
&lt;p&gt;If you ignore CDNs and similar in-house solutions for static content, in terms of hardware you only have one set of machines. Each of those machines is either on one version or another.  With the other style of &amp;#8220;all upgrade at once&amp;#8221; solutions, you will need two sets of machines, if you want to minimize downtime to seconds.&lt;/p&gt;
&lt;h2&gt;The Developers View&lt;/h2&gt;
&lt;p&gt;From the developers point of view, there is a chance that they can bring up a larger chunk of the system on their own box.  Not only does this help a desk-check of the functionality that is about to be committed, but support folks are going to find it easier to attach an &lt;span class="caps"&gt;IDE&lt;/span&gt; for interactive debugging of issues they are trying to reproduce.  Sometimes dev boxes given out in enterprises are particularly powerful.  Anything to relieve the amount of resources needed for the processes needed to stand up the stack, would be a good thing.  Also, having the production configuration closer to the development config, is a good thing.  In a way, this is faithfully scaling the production architecture &lt;strong&gt;downwards&lt;/strong&gt;, to an individual developer.&lt;/p&gt;
&lt;h2&gt;Caching and Latency Considerations&lt;/h2&gt;
&lt;p&gt;While this blog entry promotes the simplicity aspects of deploying in a cookie cutter style, there are a couple of other things to think about.&lt;/p&gt;
&lt;h3&gt;Caching Issues&lt;/h3&gt;
&lt;p&gt;The designers of the reference multi-tier stack at the top, may have caching in place at any of the tiers to reduce the number of downstream calls. That could either be a separate cache per node because there is only a fixed set of relatively static data (refer to the traditional use of &lt;a href="http://ehcache.org/"&gt;EhCache&lt;/a&gt;), or something in a &lt;a href="http://en.wikipedia.org/wiki/Memcached"&gt;Memcached&lt;/a&gt; style design that could support all of the nodes in the same tier.  This all amounts to a performance boost to the stack.  The suggested cookie-cutter solution can similarly benefit from caching.  Depending on the variable range of items being cached, and the numbers of keys, the same caching solutions can apply.&lt;/p&gt;
&lt;h3&gt;Latency Issues&lt;/h3&gt;
&lt;p&gt;It is true to say that each physically separated tier in a production stack can add latency to the end-user experience.  Caching, as mentioned, can reduce that, but only for transactions that are meaningfully cacheable.  What sort of figures can this amount to in a tuned production stack?  It can be as low as single-digit milliseconds per tier, and many would argue that this verging on insignificant.  What makes the &lt;span class="caps"&gt;TCP&lt;/span&gt;/IP multi-tier physically-separated machine style more complex in terms of design and deployment, is that care has to be taken with timeouts.  If the bottom most tier can handle timeouts appropriately, then a tier above that calls it should have a longer time out.  Or put another way, the closer to the client (browser) it gets, the longer the timeout should be.  Perhaps they &lt;strong&gt;should not be&lt;/strong&gt; inordinately long.  Instead a sensible design should occur, with shorter and shorter timeouts towards the bottom of the stack.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Once you have matched your development and deployment practices to the cookie cutter style, you are able to feel the benefits of provisioning interchangeable machines. Not only that, but the flexible resource allocation, automation of server management tasks, far fewer involved deployment engineers should provide additional (smug) comforts :)&lt;/p&gt;
&lt;h2&gt;PS &amp;#8211; Related Blog Entries&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve blogged in and around this space for a few months:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://paulhammant.com/2011/11/22/google-accounts-architectural-meaning/"&gt;The Architectural meaning behind Google&amp;#8217;s Accounts system&lt;/a&gt;&lt;/p&gt;
&lt;p style="padding-left:3em;"&gt;- Google are likely not calling side systems with every request.  There&amp;#8217;s a notable exception with Search&amp;#8217;s  &lt;a href="http://googlesystem.blogspot.com/2006/07/google-onebox-results.html"&gt;OneBox&lt;/a&gt; which is essentially a server-side include. It has to be said that there are less &amp;#8216;OneBox inserts&amp;#8217; into pages today than in 2006 when the linked blog entry was written.  Aggressive caching of the JavaScript fragments no doubt alleviate a lot of the latency that would be implicit otherwise &amp;#8211; Google really care about every millisecond in their stack, especially for search.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://paulhammant.com/2011/09/21/a-forgotten-aspect-of-the-facade-pattern/"&gt;A forgotten aspect of the Facade Pattern&lt;/a&gt;&lt;/p&gt;
&lt;p style="padding-left:3em;"&gt;- Try not to make your stack function like a sewing machine up and down the stack, for a single user transaction.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://paulhammant.com/2011/08/06/five-port-rule/"&gt;The Five port rule for stack configuration&lt;/a&gt;&lt;/p&gt;
&lt;p style="padding-left:3em;"&gt;- Merge service endpoints and avoid port-centric configuration bloat.&lt;/p&gt;
&lt;h2&gt;Thanks to..&lt;/h2&gt;
&lt;p&gt;Charles Haynes and Cosmin Stejerean for comments and fixes.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/C_cU5WCptcU" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/11/29/cookie-cutter-scaling/</feedburner:origLink></item><item><title>Google's accounts system - architectural meaning</title><link>http://feedproxy.google.com/~r/paulhammant/~3/c18Z97crZ18/</link><pubDate>Tue, 22 Nov 2011 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/11/22/google-accounts-architectural-meaning</guid><description>&lt;p&gt;Google provides hundreds of web applications. Many are merged into its google.com domain, but most exist on their own domain like blogger.com or analytics.google.com.  They want one mechanism to login across all web-apps for multiple reasons.  The reason I would like to concentrate on, for this blog article, is the desire to be able to perform a lock-step upgrade of &amp;#8220;accounts&amp;#8221; for all web-apps.  In order to achieve this they have not embedded core account logic in each separately released web-application.  Instead they redirect from the web-app in question to accounts.google.com and back again after successful authentication, if the user is not logged in.  If the user is logged into &amp;#8216;accounts&amp;#8217; but not the web-app in question, the redirect to accounts and back again can be invisible.&lt;/p&gt;
&lt;h2&gt;Visiting a Google site with no pre-existing cookies.&lt;/h2&gt;
&lt;p&gt;Google&amp;#8217;s Blogger site is a good place to examine here as it is fairly basic (sorry Blogger team!) in terms of technology.  I&amp;#8217;m using a Plugin for Firefox called &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/httpfox/"&gt;HttpFox&lt;/a&gt; to show the sequence of GETs and POSTs from the browser&amp;#8217;s point of view, but there are other ways of getting the same thing:&lt;/p&gt;
&lt;table&gt;
	&lt;tr&gt;
		&lt;th&gt;&lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Arriving At&lt;/strong&gt; &lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Cookies?&lt;/strong&gt; &lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Consequence&lt;/strong&gt; &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;1   &lt;/td&gt;
		&lt;td&gt;http://blogger.com&lt;/td&gt;
		&lt;td&gt; none &lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;2&lt;/td&gt;
		&lt;td&gt;http://www.blogger.com&lt;/td&gt;
		&lt;td&gt; none &lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com/home&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;3&lt;/td&gt;
		&lt;td&gt;http://www.blogger.com/home&lt;/td&gt;
		&lt;td&gt; none &lt;/td&gt;
		&lt;td&gt; redirect to https://www.google.com/accounts/ServiceLogin?service=blogger&amp;amp;passive=1209600&amp;amp;continue=[redirect info]&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;4&lt;/td&gt;
		&lt;td&gt;https://www.google.com/accounts/ServiceLogin?service=blogger&amp;amp;passive=1209600&amp;amp;continue=[redirect info]&lt;/td&gt;
		&lt;td&gt; A couple of accounts.google.com cookies for the session&lt;/td&gt;
		&lt;td&gt; Page load&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;5&lt;/td&gt;
		&lt;td&gt;Other Page Assets from www.google.com&lt;/td&gt;
		&lt;td&gt; none &lt;/td&gt;
		&lt;td&gt; Loaded into page&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;6&lt;/td&gt;
		&lt;td&gt;Other Page Assets from accounts.google.com&lt;/td&gt;
		&lt;td&gt; Those two accounts.google.com cookies, valid for the session &lt;/td&gt;
		&lt;td&gt; Loaded into page&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;We are presented with the login page now.  It is refers to Blogger as an ultimate destination because of a &amp;#8216;service=blogger&amp;#8217; reference in the &lt;span class="caps"&gt;URL&lt;/span&gt;.  When performing login on behalf of other google web-apps, it makes more of an effort to present itself as part of that app, by having more closely copied styling.&lt;/p&gt;
&lt;p&gt;Anyway, assume we enter correct Google-Account details, and submit the form:&lt;/p&gt;
&lt;table&gt;
	&lt;tr&gt;
		&lt;th&gt;&lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Arriving At&lt;/strong&gt; &lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Cookies?&lt;/strong&gt; &lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Consequence&lt;/strong&gt; &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;7&lt;/td&gt;
		&lt;td&gt;https://accounts.google.com/ServiceLoginAuth (&lt;span class="caps"&gt;POST&lt;/span&gt;)&lt;/td&gt;
		&lt;td&gt; none &lt;/td&gt;
		&lt;td&gt;Redirect to: https://accounts.google.com/CheckCookie?continue=[redirect info]&amp;amp;service=blogger&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;8&lt;/td&gt;
		&lt;td&gt;https://accounts.google.com/CheckCookie?continue=[redirect info]&amp;amp;service=blogger&lt;/td&gt;
		&lt;td&gt; blogger_SID=DQ-200CHARS-Ic (for .blogger.com domains)&lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com/home?auth=DQ-200CHARS-Ic&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;9&lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com/home?auth=DQ-200CHARS-Ic&lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com/home&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;10&lt;/td&gt;
		&lt;td&gt;http://www.blogger.com/home&lt;/td&gt;
		&lt;td&gt;blogger_TID=2344333a78ea3a56, blogger_TID=cedf4444e637f4e0 (both for www.blogger.com) S=blogger=ssS7uFM-btx_UR5555Uo3w (for .blogger.com)&lt;/td&gt;
		&lt;td&gt; Page loaded&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Note that the DQ-200CHARS-Ic &amp;#8216;value&amp;#8217; is a shorter representation of something that is 204 chars long,&lt;/p&gt;
&lt;h2&gt;Visiting a Google site with pre-existing cookies.&lt;/h2&gt;
&lt;table&gt;
	&lt;tr&gt;
		&lt;th&gt;&lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Arriving At&lt;/strong&gt; &lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Cookies?&lt;/strong&gt; &lt;/th&gt;
		&lt;th&gt;&lt;strong&gt;Consequence&lt;/strong&gt; &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;1&lt;/td&gt;
		&lt;td&gt;http://blogger.com&lt;/td&gt;
		&lt;td&gt; S=blogger=ssS7uFM-btx_UR5555Uo3w &lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;2&lt;/td&gt;
		&lt;td&gt;http://www.blogger.com&lt;/td&gt;
		&lt;td&gt; blogger_SID=DQ-200CHARS-Ic, S=blogger=ssS7uFM-btx_UR5555Uo3w &lt;/td&gt;
		&lt;td&gt;redirect to http://www.blogger.com/home&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;3&lt;/td&gt;
		&lt;td&gt;http://www.blogger.com/home&lt;/td&gt;
		&lt;td&gt; blogger_SID=DQ-200CHARS-Ic, blogger_TID=cedf4444e637f4e0, S=blogger=ssS7uFM-btx_UR5555Uo3w &lt;/td&gt;
		&lt;td&gt; Page load&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
&lt;h2&gt;Trying that again after deleting cookies	.&lt;/h2&gt;
&lt;p&gt;Deleting &amp;#8216;blogger_SID&amp;#8217; (204 chars) while away from blogger.com, then returning causes a redirect through http://accounts.google.com to reacquire the cookie.&lt;/p&gt;
&lt;p&gt;Deleting &amp;#8216;blogger_TID&amp;#8217; while away from blogger.com, then returning doesn&amp;#8217;t cause a redirect back to google accounts, but does recreate a new value for blogger_TID (16 chars)&lt;/p&gt;
&lt;p&gt;Deleting &amp;#8216;S&amp;#8217; while away from blogger.com, then returning doesn&amp;#8217;t cause a redirect back to google accounts, but does recreate a new value for &amp;#8216;S&amp;#8217; (23 chars ignoring blogger= prefix)&lt;/p&gt;
&lt;p&gt;Deleting all three cookies, then returning to blogger.com, causes a redirect through http://accounts.google.com to reacquire all three.&lt;/p&gt;
&lt;p&gt;In terms of values, only blogger_SID (the 204 char one) is consistent.  The other ones are regenerated each time.&lt;/p&gt;
&lt;h2&gt;What we have learned about Google&amp;#8217;s auth cookies&lt;/h2&gt;
&lt;p&gt;The blogger_TID and S cookies contain keys that refer to something internal inside the Blogger platform.  The blogger_SID key contains much more information about &amp;#8216;me&amp;#8217; in the context of google accounts.  I think it contains some personal information (my email address, whether I&amp;#8217;m part of a &amp;#8220;Google Apps For Your Domain&amp;#8221; account in this context), and some context about when the authentication occurred, as well as a checksum.   That cookie&amp;#8217;s expiry is in 2019, so it isn&amp;#8217;t part of the authentication (sensibly).  The cookie&amp;#8217;s payload, which I shortened from 204 chars to &amp;#8216;DQ-200CHARS-Ic&amp;#8217;, is encrypted of course.&lt;/p&gt;
&lt;p&gt;As an aside, Google lost their private key for that in a hacking incident (a Windows user innocently installed a back-door or root-kit thing inside their MountainView campus) some years ago.  They had to quickly invalidate all auth cookies everywhere, after choosing a new private key, after ensuring their deployment infrastructure was &amp;#8216;safe&amp;#8217; as well as actually eliminating the back-door.  I&amp;#8217;d link to the article, but Google&amp;#8217;s search isn&amp;#8217;t finding it.&lt;/p&gt;
&lt;h2&gt;Architectural meaning of Google&amp;#8217;s auth cookie design.&lt;/h2&gt;
&lt;p&gt;Blogger.com is going to get all three cookies for each request that comes in.  Depending on the &lt;span class="caps"&gt;URL&lt;/span&gt;, that could mean for static assets like Jpegs and JavaScript too [See PS. below].  If the back-end Google application can decrypt the main &amp;#8216;blogger_SID&amp;#8217; cookie (it can), and the checksum matches, it can trust the details within without referring to any account services internally as part of that transaction.  If the backend decides that the cookie is wrong or missing, it will redirect through accounts.google.com.  From the user point of view the redirect only happens once in a while.  It&amp;#8217;s hardly time-consuming anyway as Google apps are tuned for extreme performance for the most part.  The redirect can happen when a timeout occurs &amp;#8211; the cookie is decrypted and its internal not-valid-after has been exceeded.   The redirect can also happen if someone in the security team has hit a &amp;#8220;Untrust all auth cookies&amp;#8221; button centrally, which probably pushes a signal to all stood-up application servers.&lt;/p&gt;
&lt;p&gt;Here is the one-line summary &amp;#8211; &lt;strong&gt;Google&amp;#8217;s secure cookies for an authenticated user allow Google apps to save milliseconds not authenticating every request, while still having a strong centralized accounts service, and the ability to cancel cookies anywhere at any time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you are making multiple, separately deployable, sites for your enterprise, you should think about doing the same, instead of replicating a /accounts/Login.do in each application.  Authentication is a special case though.  Indeed Google is a special case though, as Steve Yegge lamented recently amongst &lt;a href="http://news.ycombinator.com/item?id=3101876"&gt;other thoughts in a long piece&lt;/a&gt;. I&amp;#8217;ll say some more about the general case for shared services in my next blog entry.&lt;/p&gt;
&lt;p&gt;PS &amp;#8211; I tried to convince the &lt;span class="caps"&gt;IETF&lt;/span&gt; team concerned with the cookie spec, that a useful change could be made. &lt;a href="http://www.ietf.org/mail-archive/web/http-state/current/msg01116.html" title="be sure to read the three replies to that thread"&gt;See my failure to do so on their mail-list&lt;/a&gt; In the above case, after the page load, Blogger loads 15 static assets all of which receive the same three cookies but do not need to.  Google are known for obsessing about unnecessary bytes, so I&amp;#8217;m surprise they&amp;#8217;ve not approached the &lt;span class="caps"&gt;IETF&lt;/span&gt; cookie steering committee.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/c18Z97crZ18" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/11/22/google-accounts-architectural-meaning/</feedburner:origLink></item><item><title>Reporting Selenium2 (WebDriver) bugs</title><link>http://feedproxy.google.com/~r/paulhammant/~3/DT3_KttLS2Q/</link><pubDate>Mon, 14 Nov 2011 00:00:00 PST</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/11/14/reporting-selenium2-bugs</guid><description>&lt;p&gt;This has come up again and again on the Selenium user list and when liaising with clients that are using Selenium:  &lt;strong&gt;How to report bugs to the Selenium team?&lt;/strong&gt;.  The problem is more subtle than you would think.  Selenium2 can be driven by a number of languages, has multiple browsers that can be driven, and tastes around build technologies vary (especially within the dev team).&lt;/p&gt;
&lt;h2&gt;Groovy scripts are the most sharable, terse yet capable in my opinion&lt;/h2&gt;
&lt;p&gt;Consider this example for using Firefox for searching for &amp;#8220;Selenium&amp;#8221; on the ThoughtWorks site:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="nd"&gt;@Grapes&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;
  &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;org.seleniumhq.selenium:selenium-java:2.12.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="nd"&gt;@Grab&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;junit:junit:4.7&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;])&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.firefox.FirefoxDriver&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openqa.selenium.By&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;junit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertTrue&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FirefoxDriver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;http://www.thoughtworks.com&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;searchField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;edit-search-block-form-1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;searchField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Selenium&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;searchBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;edit-submit&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;searchBtn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPageSource&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;to run the same acceptance tests&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;WebDriver test complete&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The bug-reporter are able to capture everything about the problem in a single script, with no build-file dependency.  They can juggle the version of Selenium quite easily in the @Grab line at the top of the file.  That might be important if the bug exists in one version of Selenium2 but not another.  If the user is familiar with xUnit style programming they can keep lines similar to the assertTrue(&amp;#8230;) line, but if they are not, they can code something rudimentary themselves.  As this gets uploaded to the Selenium project issue tracker, it does not matter whether it is an attachment, or cut/paste into the body of the issue.  The bug-reporter may also be pleased to know that Groovy itself doesn&amp;#8217;t really need to be installed: see later.&lt;/p&gt;
&lt;p&gt;From the point of view of the selenium team (who on average are pretty good at Java and hence Groovy too), the script can be run more easily than anything that has a build file, or is just a fragment of logic.  The @Grapes business means that only Groovy is a prerequisite install beyond the Java install they already have.  Grapes will download the dependencies (somewhat silently) if they are no cached and puts them in [your home directory]/.ivy/ making it easy.  Lastly, the Selenium developer will be able to attach a debugger as easily as they would to a Java process.&lt;/p&gt;
&lt;h2&gt;Proprietary nature of pages?&lt;/h2&gt;
&lt;p&gt;Some clients don&amp;#8217;t want their pages to appear in public before they are ready.  In that case you need to consider trusting one or two members of the Selenium dev team with a single problematic page from your being-built application.  The &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/mozilla-archive-format/"&gt;&lt;span class="caps"&gt;MAFF&lt;/span&gt; plugin for Firefox&lt;/a&gt; will do admirable job of saving a page including state and scripts.  You could mount the result of a save on an obscure &lt;span class="caps"&gt;URL&lt;/span&gt;, or send it as a zip to the developer handing your bug report (presumably after a conversation in email).  The &lt;span class="caps"&gt;MAFF&lt;/span&gt; trick is a neat one, as it allows you to omit mention of any pages and Selenium operations that are not directly related to the bug in question.  Even if your preferred browser is Internet Explorer, this Firefox workflow will save a site that is perfectly navigable by IE.&lt;/p&gt;
&lt;h2&gt;Getting up to speed with Groovy.&lt;/h2&gt;
&lt;p&gt;Java is required by Groovy. It is Windows, Mac and Linux compatible.  It&amp;#8217;s already on Mac versions up to Snow Leopard.  For Lion, you have to &lt;a href="http://decoding.wordpress.com/2011/07/21/mac-os-x-lion-how-to-install-java/"&gt;install it yourself&lt;/a&gt; .  For Windows and Linux, go to &lt;a href="http://www.java.com/en/download/"&gt;Java.com&lt;/a&gt; and click through to the right download.  If it&amp;#8217;s asking you whether you want &amp;#8220;runtime environment&amp;#8221; or &amp;#8220;developer kit&amp;#8221; choose the latter.  Test Java on the command line like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; java -version
java version &lt;span class="s2"&gt;&amp;quot;1.6.0_29&amp;quot;&lt;/span&gt;
Java&lt;span class="o"&gt;(&lt;/span&gt;TM&lt;span class="o"&gt;)&lt;/span&gt; SE Runtime Environment &lt;span class="o"&gt;(&lt;/span&gt;build 1.6.0_29-b11-402-10M3527&lt;span class="o"&gt;)&lt;/span&gt;
Java HotSpot&lt;span class="o"&gt;(&lt;/span&gt;TM&lt;span class="o"&gt;)&lt;/span&gt; 64-Bit Server VM &lt;span class="o"&gt;(&lt;/span&gt;build 20.4-b02-402, mixed mode&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A reboot may be necessary, to get Java&amp;#8217;s &amp;#8216;bin&amp;#8217; directory into the &lt;span class="caps"&gt;PATH&lt;/span&gt; environmental variable.  If you&amp;#8217;re advanced with such things, you already know about tricks to negate the need for the reboot.&lt;/p&gt;
&lt;p&gt;For Groovy itself, things are slightly simpler.  Download the &amp;#8220;binary release&amp;#8221; from the &lt;a href="http://groovy.codehaus.org/Download?nc"&gt;Groovy website&lt;/a&gt;  Unzip it and make sure the pertinent &amp;#8216;bin&amp;#8217; directory is in your path.  Test it like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; groovy --version
Groovy Version: 1.8.4 JVM: 1.6.0_29
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;p&gt;PS &amp;#8211; Groovy can be terser still than the test-script represented above &amp;#8211; I just didn&amp;#8217;t want to scare anyone :)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/DT3_KttLS2Q" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/11/14/reporting-selenium2-bugs/</feedburner:origLink></item><item><title>Locked US iPhones suck</title><link>http://feedproxy.google.com/~r/paulhammant/~3/_X-c42-dNgY/</link><pubDate>Fri, 21 Oct 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/10/21/locked-us-iphones-suck</guid><description>&lt;p&gt;I had the original iPhone via AT&amp;amp;T in the US.  I didn&amp;#8217;t wait in line, as I didn&amp;#8217;t buy on day one, but that&amp;#8217;s an aside.  Why are these buggers locked in the US? My experience with other phones and other jurisdictions is that they are not, or at least they are not after the contract expires. (Apologies for the &amp;#8216;first world problem&amp;#8217; blog entry).  In other countries, you can bring your unlocked smartphone to a cheaper monthly account (no phone subsidies being paid off).&lt;/p&gt;
&lt;h2&gt;People who spend more than a week out of the &lt;span class="caps"&gt;USA&lt;/span&gt; in a year (like me)&lt;/h2&gt;
&lt;p&gt;We want to &lt;span class="caps"&gt;SIM&lt;/span&gt; swap rather than suffer roaming charges.  That&amp;#8217;s not just for voice, that&amp;#8217;s for data too.  I like using Google Maps to traverse the UK with.  I can&amp;#8217;t do that with a locked US iPhone.  There mush be a bunch of traveling Europeans like me who&amp;#8217;d like to availing of the cheaper temporary plans in their &amp;#8216;home&amp;#8217; countries before returning to the states to work. A &amp;#8216;Pay as you go&amp;#8217; account in the UK that works with my iPhone costs me about $100 for a whole year (I&amp;#8217;m only there for three separate weeks).  That&amp;#8217;s with data!&lt;/p&gt;
&lt;p&gt;Power travelers are the same.  Roy Singham, the ThoughtWorks owner, probably has a &lt;span class="caps"&gt;SIM&lt;/span&gt; for every country he regularly goes to. There are many executive types that are the same.  For him it is about data+voice too.&lt;/p&gt;
&lt;h2&gt;What we know about unlocked iPhones.&lt;/h2&gt;
&lt;p&gt;&lt;img src="http://cultofmac.cultofmaccom.netdna-cdn.com/wp-content/uploads/2009/08/official_iphone_3g_unlock.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;They are in a database that Apple keeps.  That could even be a spreadsheet for all we know, but all the purchased-as-unlocked iPhones are in it.  The ones that are unlocked after purchase are added to it, and there&amp;#8217;s a mechanism via iTunes to action the unlock and notify customers that &lt;a href="http://www.cultofmac.com/14923/apple-finland-officially-unlocks-one-lucky-bloggers-iphone/"&gt;it happens elsewhere&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;AT&amp;amp;T&amp;#8217;s iPhones.&lt;/h2&gt;
&lt;p&gt;These have always been locked; Permanently so. At least the subsidized ones are. You can pay premium for an unlocked one, but there&amp;#8217;s no cheaper monthly fee from them if your do. They are so from &amp;#8216;birth&amp;#8217; to when you&amp;#8217;ve inevitable sat on it, dropped it, or seen it fade into obsolescence/disfunction after some years.  It is rumored that AT&amp;amp;T hardly make any money from the iPhone.  More specifically, the monthly fee barely covers the ongoing costs and subsidized hardware.&lt;/p&gt;
&lt;h2&gt;Me trying to unlock my end-of-contract 3GS.&lt;/h2&gt;
&lt;p&gt;So as is the custom in other countries for other phones, I&amp;#8217;ve tried to call the teleco to get it unlocked.  Yesterday I spoke for three AT&amp;amp;T employees via their &amp;#8216;611&amp;#8217; support service, and one Apple employee who AT&amp;amp;T transferred me to. All people were helpful, and incredibly polite even though I was trying to push my point &amp;#8220;please could you unlock my end of contract iPhone 3GS&amp;#8221;.  I&amp;#8217;ve been unsuccessful in that endeavor, but I&amp;#8217;ve found out some things that are worth writing about.&lt;/p&gt;
&lt;p&gt;In initial call to AT&amp;amp;T I was assured that only Apple can unlock iPhones.  That&amp;#8217;s true, I said, but only on request from the teleco in question.  We didn&amp;#8217;t agree, so I was transferred to Apple. That fellow confirmed what I had previously thought, and we completed the call at that point.&lt;/p&gt;
&lt;p&gt;My second call to AT&amp;amp;T took be through the same process, but I managed to get bumped to a manager, after thanking the associate for her help and caring about the issue.  The manager I reached was very sympathetic to my wish, and ultimately asked a couple of back office techies who&amp;#8217;d really know, and the answer came back:&lt;/p&gt;
&lt;p&gt;&amp;#8212;&amp;gt; &lt;strong&gt;AT&amp;amp;T do not know how to initiate the electronic iPhone unlock via Apple&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It seems to me that Apple could either unilaterally unlock end of contract iPhones if that were true, or send training materials to AT&amp;amp;T so that they can gain the capability.&lt;/p&gt;
&lt;h2&gt;Sprint&amp;#8217;s iphone (4S)&lt;/h2&gt;
&lt;p&gt;Just before the launch of the 4S, we thought Sprint may unlock them on request.  Sprint has allegedly looked at the $4Bn fee they handed to Apple though, realized they need to double their subscriber pool to cover it. That means they need revenue from the same iPhone after the 24th month that would be the completion of the contract with the customer.  Whether that is your extended contract, or your Mom after giving her the phone, they don&amp;#8217;t care.  As with AT&amp;amp;T you don&amp;#8217;t get a reduction in monthly fee, and that is when their real profits start.&lt;/p&gt;
&lt;h2&gt;Verizon.&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://www.zdnet.com/blog/cell-phones/international-travelers-here-is-how-to-sim-unlock-your-verizon-apple-iphone-4s/6712"&gt;They will unlock for customers who have &amp;#8216;accounts in good standing&amp;#8217;&lt;/a&gt;.  That reasonably means that Verizon think of you as someone who paid your bills on time for 60 days or so.&lt;/p&gt;
&lt;h2&gt;Value of end-of-contract iPhones.&lt;/h2&gt;
&lt;p&gt;Given the free 3GS phones available now, and the fact that Apple release a new iPhone every 14 months, the iPhone you buy today is not going to have much resale value.  If the phone is locked to AT&amp;amp;T and new customers could add a new one for free, what value is there in an end of contract iPhone?  It is zero really for US markets. A silver-lining is that the iPhone could be unlocked for a new life abroad, and for that there&amp;#8217;s some value.  That unlock is likely to be some unapproved means, and there&amp;#8217;s no guarantee that Apple wouldn&amp;#8217;t be able to close down the unlock/jailbreak in successive iOS releases. Actually that hypothetical re-closure is more about jail-breaking.&lt;/p&gt;
&lt;p&gt;So a legitimately and permanently unlocked iPhone has extra value, once you&amp;#8217;re hellbent on upgrading and selling your old iPhone.  No-one has to fear upgrading iOS versions and the possibility of it not working as wished for after.&lt;/p&gt;
&lt;h2&gt;Conclusion.&lt;/h2&gt;
&lt;p&gt;I think us foreigners living in the &lt;span class="caps"&gt;USA&lt;/span&gt; are fools to go with AT&amp;amp;T iPhones right now. We should go with Verizon and unlock after 60 days.  The &lt;span class="caps"&gt;GSM&lt;/span&gt; side will work in UK, France, Germany, Brazil (etc) with some modest &lt;span class="caps"&gt;SIM&lt;/span&gt; swapping. We get the best of both worlds.  Verizon should not worry as we will pay our two years of fees for US &lt;span class="caps"&gt;CDMA&lt;/span&gt; usage. It is just that we want to &lt;span class="caps"&gt;SIM&lt;/span&gt;-swap for a few weeks a year, when &amp;#8216;back home&amp;#8217;. As I said before it not just about voice any more; It is data too.&lt;/p&gt;
&lt;p&gt;I wonder if people who have already started their AT&amp;amp;T iphone 4S plans two weeks ago shouldn&amp;#8217;t cancel (within the 30 days) and reorder a Verizon edition.&lt;/p&gt;
&lt;h2&gt;Toothless regulators.&lt;/h2&gt;
&lt;p&gt;The &lt;a href="http://www.fcc.gov"&gt;&lt;span class="caps"&gt;FCC&lt;/span&gt;&lt;/a&gt; is the regulator here in the &lt;span class="caps"&gt;USA&lt;/span&gt;, and historically they do not step into contractual imbalances.  Compare them to &lt;a href="http://www.ofcom.org.uk"&gt;&lt;span class="caps"&gt;OFCOM&lt;/span&gt;&lt;/a&gt; (UK) and other European bodies who have forced cheapening changes on their country&amp;#8217;s telecos.&lt;/p&gt;
&lt;p&gt;PS &amp;#8211; I do not care about data+voice concurrently feature, or the &amp;#8216;higher speeds&amp;#8217; that AT&amp;amp;T have (that Verizon do not).&lt;/p&gt;
&lt;h2&gt;Addendum (Nov 15, 2011)&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m safely on a Verizon iPhone now.  Not being able to surf while making a call is the hypothetical downside. Well that and Verizon&amp;#8217;s iPhone4s network speeds being slower than AT&amp;amp;T 3.5G one. Fast enough to watch movies though, so why would I want it faster??&lt;/p&gt;
&lt;p&gt;AT&amp;amp;T are charging me a $60 early termination fee given I cancelled ten or so days before my natural end of two-year contract. &amp;#8220;Michael&amp;#8221; (a helpful fellow in the call center) said they can setup a forward note for themselves to credit me that amount seeing as my normal last month charge should apply, but their system is inflexible (my words) enough to make that a manual human step.  I&amp;#8217;ll make a calendar note.  I also passed on my rationale for canceling [ this whole posting Michael if you&amp;#8217;re reading ], and I got to hear again that Apple hasn&amp;#8217;t provided a mechanism to unlock iPhones.  I&amp;#8217;m suspecting that&amp;#8217;s a scripted response.  Bye bye AT&amp;amp;T, sorry that you feel the need to gouge customers in their 25th month and onwards.&lt;/p&gt;
&lt;h2&gt;Update (April 22, 2012)&lt;/h2&gt;
&lt;p&gt;Verizon iPhone 4S unlocked after a simple 5 mins phone call.  AT&amp;amp;T change their mind &amp;#8211; I hope I helped mount that pressure.  Will be testing it soon with the Wife&amp;#8217;s 3GS which is out of contract.  Also meeting up with buddies recently, I was taunted for having a slower 4S service through Verizon.  We did a quick pub-test &amp;#8211; watching the same movie clip on Youtube at the same time &amp;#8211; there was no difference :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/_X-c42-dNgY" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/10/21/locked-us-iphones-suck/</feedburner:origLink></item><item><title>Mozilla are screwing corporate adopters with their numbering scheme</title><link>http://feedproxy.google.com/~r/paulhammant/~3/oeY8G9sHtfA/</link><pubDate>Wed, 12 Oct 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/10/12/Mozilla-screwing-corporate-adopters-with-their-numbering-scheme</guid><description>&lt;p&gt;I cannot be the first to say this, but Mozilla by flipping through Firefox 3, 4, 5, 6, 7 every six weeks or so are really screwing their corporate adoption fans.&lt;/p&gt;

&lt;p&gt;Why? For an arbitrary series of Internet Explorer 8.x (or 6.x, or 9.x etc), laggardly corporations could state &amp;#8220;our internal web applications will be compatible with all 8.x releases of Internet Explorer&amp;#8221;. They can say that with modest certainty because the layout engine (or other major components of browser) will no be rewritten between 8.0 and 8.1. They then choose to fix at the 8.x, and accept patches within that series.&lt;/p&gt;

&lt;p&gt;However Mozilla are giving no ability for corporate types to fix at major versions, nor any guarantees around when major components will be rewritten.&lt;/p&gt;

&lt;p&gt;At a smaller scale we are already seeing issues with the availability of Firefox plugins for the cutting edge releases. Previously, plugin-makers could mark their products as compatible with a series like 3.x and relax somewhat. Nowadays they have to really watch the release schedule for Firefox and ship accordingly, or aim at a incredibly broad range like 3.x -&amp;gt; 15.x and ignore the fact that that is an egregious claim.&lt;/p&gt;

&lt;p&gt;As an aside, it would be nice for &lt;a href='http://saucelabs.com'&gt;Sauce Labs&lt;/a&gt; to deploy a &amp;#8216;daily Firefox&amp;#8217; build, that customers could run test suites against. Corporate types would be able to feel more comfy with Firefox then, and offer greater assurances about compatibility for internal going forward. External apps might also benefit, obviously.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/oeY8G9sHtfA" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/10/12/Mozilla-screwing-corporate-adopters-with-their-numbering-scheme/</feedburner:origLink></item><item><title>setContext(..) is back for Selenium2 :-P</title><link>http://feedproxy.google.com/~r/paulhammant/~3/8Rfx932uNs8/</link><pubDate>Fri, 30 Sep 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/09/30/setContext-back-for-selenium2</guid><description>&lt;p&gt;Of course, Simon objects so it&amp;#8217;s not actually in the core API, and it&amp;#8217;s only back for Sauce Labs operation, via a hack :)&lt;/p&gt;

&lt;p&gt;See a &lt;a href='https://saucelabs.com/jobs/aca77232d931824c618ef9b34200fe09'&gt;Sauce Labs job&lt;/a&gt; that shows it off in use for a BDD/JBehave script.&lt;/p&gt;

&lt;p&gt;To use, just do something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;JavascriptExecutor js = (JavascriptExecutor) webDriver;
js.executeScript(&amp;quot;sauce:context=the sentence you would like to appear in Sauce Labs&amp;#39; job log&amp;quot;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sauce Labs (and others) can possibly use executeScript() as a generalized control API for their remote cloud instances.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/8Rfx932uNs8" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/09/30/setContext-back-for-selenium2/</feedburner:origLink></item><item><title>Branchable Continuous Integration</title><link>http://feedproxy.google.com/~r/paulhammant/~3/DCVi6bEZUes/</link><pubDate>Fri, 30 Sep 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/09/30/branchable-continuous-integration</guid><description>&lt;p&gt;Most folks know that I like trunk-based-development, with branches made in advance of release.&lt;/p&gt;

&lt;p&gt;Less well known is that I like my Continuous Integration configuration to be &lt;a href='http://paulhammant.com/blog/branch_by_abstraction.html'&gt;under source control too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve spent a lot of time in Jenkins recently configuring job, then copy-pasting that (after multiple clicks to navigate around) into similar jobs.&lt;/p&gt;

&lt;p&gt;I would really like to be able to do all of that in Intellij, and just commit atomically, possibly in the same commits as regular changes. I&amp;#8217;d also like to be able to make branches and push them to within the visibility of the CI master daemon, and it provision the implicit pipelines from that commit automatically.&lt;/p&gt;

&lt;p&gt;What that means for me is that the CI tool should watch all commits, and be able to recognize its configuration files amongst the commits (new, changed and deleted) and reconfigure itself before it leaps into action to process the particular commit(s).&lt;/p&gt;

&lt;p&gt;Cruise-Control was close in a bygone era. Indeed, I remember working with &lt;a href='http://www.pauljulius.com/'&gt;Paul Julius&lt;/a&gt; in 2005 to rework the XML-Include capability to be resilient to botched edits. That last would mean that the reconfig would not take down a &lt;strong&gt;whole&lt;/strong&gt; pipeline if one dev forgot an angle bracket.&lt;/p&gt;

&lt;p&gt;I wish the modern CI tools could be configured completely using commits, including new pipelines.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/DCVi6bEZUes" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/09/30/branchable-continuous-integration/</feedburner:origLink></item><item><title>Automatic sequence diagrams from BDD-scenario executions</title><link>http://feedproxy.google.com/~r/paulhammant/~3/khNUHSaG4YU/</link><pubDate>Fri, 23 Sep 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/09/23/auto-sequence-diagrams-from-BDD-scenarios</guid><description>&lt;p&gt;They would be great &amp;#8211; right?&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.websequencediagrams.com/cgi-bin/cdraw?lz=U2NlbmFyaW8tPlN0ZXA6IEdpdmVuIEkgYW1cbm1hbmFnaW5nXG5teSBhY2NvdW50Cm5vdGUgcmlnaHQgb2YgAC8GTXlTdGVwcwpTdGVwLT5QYWdlOiBlbnRlciBuYW1lXG5hbmQgbG9naW4gADAPACUGTG9naW5QYWdlClBhZ2UtPlNlbGVuaXVtOiBmaWxsIGluXG4jbmFtZQoAEQgtPkJyb3dzZXI6IGZpbmQgAAUZdHlwZWtleXMgeHgARBggI3Bhc3N3b3JkAEUacHcAQB15eQCBbQ1zdWJtaXQAgTsRABAHAIExEwAqBwCBTQcgLT4gQXBwU2VydmVyOiBQT1NUXG4vAII6BQoADwkgLT4gU2VydmljZXM6AIJWBgCDChAAFQkAgloFACYH&amp;amp;s=roundgreen" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.websequencediagrams.com/?lz=U2NlbmFyaW8tPlN0ZXA6IEdpdmVuIEkgYW1cbm1hbmFnaW5nXG5teSBhY2NvdW50Cm5vdGUgcmlnaHQgb2YgAC8GTXlTdGVwcwpTdGVwLT5QYWdlOiBlbnRlciBuYW1lXG5hbmQgbG9naW4gADAPACUGTG9naW5QYWdlClBhZ2UtPlNlbGVuaXVtOiBmaWxsIGluXG4jbmFtZQoAEQgtPkJyb3dzZXI6IGZpbmQgAAUZdHlwZWtleXMgeHgARBggI3Bhc3N3b3JkAEUacHcAQB15eQCBbQ1zdWJtaXQAgTsRABAHAIExEwAqBwCBTQcgLT4gQXBwU2VydmVyOiBQT1NUXG4vAII6BQoADwkgLT4gU2VydmljZXM6AIJWBgCDChAAFQkAgloFACYH&amp;amp;s=roundgreen"&gt;See markup for this on WebSequenceDiagrams.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Should be easy enough to construct the markup language from instrumented versions of:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;JBehave or Cucumber (etc)&lt;/li&gt;
	&lt;li&gt;Selenium 2.0 (WebDriver)&lt;/li&gt;
	&lt;li&gt;Your application stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Perhaps better if the company behind the graphing technology could adjust the vertical spacing based on actual timings.  I&amp;#8217;ll email them :)&lt;/p&gt;
&lt;p&gt;PS &amp;#8211; Colleage Xiao Peng has &lt;a href="https://github.com/MrCoder/Simple-Sequence"&gt;done something similar&lt;/a&gt; that cab be played with on &lt;a href="http://seqdia.appspot.com/"&gt;AppEngine&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/khNUHSaG4YU" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/09/23/auto-sequence-diagrams-from-BDD-scenarios/</feedburner:origLink></item><item><title>A forgotten aspect of the Facade Pattern</title><link>http://feedproxy.google.com/~r/paulhammant/~3/u3OdjIWw_dM/</link><pubDate>Wed, 21 Sep 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/09/21/a-forgotten-aspect-of-the-facade-pattern</guid><description>&lt;p&gt;Use of the Facade Pattern in software is about apparent elegance on the &amp;#8216;user&amp;#8217; side of the facade and the hiding of inelegance on the &amp;#8216;implementation&amp;#8217; side.  The implementation can change or be replaced easily over time, but that applies to many patterns.  Thus it is the elegance of the facade that&amp;#8217;s making it attractive to use in a layered design.  There is a second aspect, thought, that is more subtle and often forgotten: reducing the number of calls needed through it.  Hopefully to one.&lt;/p&gt;
&lt;h2&gt;Dishonored Facade Implementations&lt;/h2&gt;
&lt;p&gt;Just pretend for a second that a object that straddles a divide has gettable and settable things that are representative of more meaningful business things:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Broken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;setGivenName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;gn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;setFamilyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;getGivenName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;getFamilyName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;What we have in the above is something where two are likely to be called in series for typical interactions.  We could see that getGivenName and getFamilyName could often be called together when populating an UI&amp;#8217;s fields.  Similarly setGivenName and setFamilyName could be called together when pushing the UI&amp;#8217;s fields back towards persistence:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="c1"&gt;// populate UI:&lt;/span&gt;

&lt;span class="nx"&gt;gn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getGivenName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFamilyName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// (elsewhere) persist UI fields:&lt;/span&gt;

&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setGivenName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setFamilyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the facade happens to by hiding inelegance of lower level implementation of things that go over a wire, we will now observe two calls over the a &lt;span class="caps"&gt;RPC&lt;/span&gt; or RESTful divide.  There are performance consequences that add up quickly.&lt;/p&gt;
&lt;h2&gt;Honoring Facade Patterns&lt;/h2&gt;
&lt;p&gt;When the facade pattern is implemented correctly, more data are channelled though fewer invocations (again these are representative of more meaningful business operations):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// imagine constructor, getter, setter that&lt;/span&gt;
  &lt;span class="c1"&gt;// are characteristic of Immutable or ValueObject&lt;/span&gt;
  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;givenName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BetterFacade&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;changeName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="nx"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;What we have now a single operation through the facade that takes or gives something that could be serialized quite easily.  Again it does not matter at this stage whether the underlying implementation is a Remote Procedure Call, or something RESTful.&lt;/p&gt;
&lt;p&gt;At this level of object granularity, it should be easy to get right.&lt;/p&gt;
&lt;h2&gt;The Dark Ages of Enterprise Computing&lt;/h2&gt;
&lt;p&gt;Sun&amp;#8217;s late-90&amp;#8217;s J2EE is the largest effort to bring bad practice to enterprise computing. Particularly the &lt;span class="caps"&gt;EJB&lt;/span&gt; stuff in its early incarnations.  Sun promoted &lt;a href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/SessionFacade.html"&gt;Session Facade&lt;/a&gt; as one of the constructs you should code in to your stack. In so doing, they forced a ton of inelegance on the developer.  The page I link to shows the J2EE definition from the 2001-2002 era (Oracle could nix it at any time). It is also in the &lt;span class="caps"&gt;EJB&lt;/span&gt; 2.0 style before &lt;span class="caps"&gt;EJB&lt;/span&gt; 3.0 injected some small amount of sanity in 2005.  From 1998 to 2004, Sun told folks to make directed graphs of components while constructing applications, that required a ton of boiler-plate code.  In the effort to just get it working many teams forgot the desire to reduce invocations across the divide.  Java teams are still perhaps forgetful in the years since the progressive abandonment of &lt;span class="caps"&gt;EJB&lt;/span&gt; in particular and J2EE generally since (the mid noughties).&lt;/p&gt;
&lt;h2&gt;Clues that you are doing it wrong.&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/facade.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;While the above diagram does not show categorically an incorrect architecture, even if the there&amp;#8217;s a &lt;span class="caps"&gt;TCP&lt;/span&gt;/IP separation between each of the boxes. It does show the start of a fan-out of connections.  What starts with one I/O from the browser to the web-tier, results in three I/Os to lower level services.  That could be nine more if each of those three did the same to layers below. We might have a constraint in that these have to be separate boxes, and we have to do I/O in series to them as part of the buyCartContents operation.  Incidentally, the Catalog node in the above diagram, could have some caching attached to reduce some of our worries, but that is a different thing.&lt;/p&gt;
&lt;p&gt;What I am trying to get across at this level of architecture, is the fact that you could quickly engineer something with a lot of downstream invocations that add up while you are otherwise busy with developing functionality.  As you&amp;#8217;re making your stack, look at the numbers of invocations across divides that could otherwise be facade-like, and worry of the growth of them is uncontrolled. Look at facade methods that are similar to each other, or always invoked in series and could be rolled into one.&lt;/p&gt;
&lt;h2&gt;One technique for doing it right from the start.&lt;/h2&gt;
&lt;p&gt;Simply put, you should rigidly stick to top-down design as you build your application stack over time.  This is true for the run up to your initial go live, and the in the incremental releases that follow that first push.  Do not let Database Administrators (DBAs) drive the design of your web-app.  Nor engineers of lower level services.  Both of these are &amp;#8216;bottom-up&amp;#8217; techniques and classic false economies for enterprise development teams. Instead think about the facade like operations from the UI, and change downstream code as appropriate to support that. If that means that your UI teams are more instructional to service teams, so be it.&lt;/p&gt;
&lt;p&gt;While adhering to the above, keep busy with your &lt;a href="http://paulhammant.com/2011/09/01/refactoring-experiment/"&gt;refactoring agenda&lt;/a&gt; , even if you are using a language that does not have an &lt;span class="caps"&gt;IDE&lt;/span&gt; as good as Intellij (Java) is. With a decent Agile perspective the right design will emerge at all times.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/u3OdjIWw_dM" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/09/21/a-forgotten-aspect-of-the-facade-pattern/</feedburner:origLink></item><item><title>What is Dart going to be?</title><link>http://feedproxy.google.com/~r/paulhammant/~3/8rRGZuQEpQw/</link><pubDate>Mon, 12 Sep 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/09/12/what-is-dart-going-to-be</guid><description>&lt;p&gt;There is not a lot written about what Dart is actually going to look like, so I will speculate a little.  ThoughtWorkers are going to cringe as I take the opportunity to bring up something I have been chasing for years.&lt;/p&gt;
&lt;h2&gt;Convergence of Declarative and Procedural UI markup languages&lt;/h2&gt;
&lt;p&gt;It has never been described as pleasant jumping from a declarative form like &lt;span class="caps"&gt;HTML&lt;/span&gt; to a turing-complete procedural form like JavaScript.  That we use an escape mechanism &amp;lt;script&amp;gt;JS here&amp;lt;/script&amp;gt; is only the first clue that the combination is as ugly as hell.  JavaScript was defined after &lt;span class="caps"&gt;HTML&lt;/span&gt;, so it is interesting to wonder what the pair would look like if JavaScript had come first.  I am sure it would look more like JavaScript and less like &lt;span class="caps"&gt;HTML&lt;/span&gt;.  The clue is the huge amount of libraries today that retrofit JavaScript with increasing elegant UI morphing capabilities.  JQuery is perhaps the best known of these, but there are others that are compelling too.&lt;/p&gt;
&lt;h2&gt;Columns of UI setup code &amp;#8211; bad bad bad&lt;/h2&gt;
&lt;p&gt;There is a style of UI programming that is common, where methods and functions separate sections of UI setup code in a classing OO design. Model View Controller (or related patterns) are possible, but the largest visual aspect to actually developing the source for these, is the long columns of &amp;#8216;object.method(params)&amp;#8217; with the occasional for loop, or if condition. Nesting is only there if the developer has not broken the setup into separate methods.&lt;/p&gt;
&lt;p&gt;Swing, &lt;span class="caps"&gt;SWT&lt;/span&gt; and &lt;span class="caps"&gt;GWT&lt;/span&gt; are all examples of this. Elaborate UIs can be constructed using them, but there&amp;#8217;s none of the elegance of the original &lt;span class="caps"&gt;HTML&lt;/span&gt;, in that nesting of the UI logic, does not map to visual nesting.  That nesting, in &lt;span class="caps"&gt;HTML&lt;/span&gt; 1.0, was the thing that allowed unskilled people to have a go at editing pages, and hitting refresh in the browser.&lt;/p&gt;
&lt;h2&gt;Some meld of &lt;span class="caps"&gt;JSON&lt;/span&gt;, closures is my hope for Dart.&lt;/h2&gt;
&lt;p&gt;Here&amp;#8217;s something that &lt;a href="http://twitter.com/#!/dudadornelles"&gt;Duda Dornelles&lt;/a&gt; put together using Node.js, that could execute directly in a browser context:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
     &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;MJS example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
         &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;header&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
             &lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nav&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                 &lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                 &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/logout&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Logout&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                     &lt;span class="p"&gt;}&lt;/span&gt;
                 &lt;span class="p"&gt;}&lt;/span&gt;
             &lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I cannort help feeling though that it is too lispy, with the current ugliness of JavaScript interspersed.  I would prefer something more like what is possible in Groovy, Ruby or Python with builders/closures:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="javascript"&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;head&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MJS example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
	        &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;header&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
	            &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;nav&amp;#39;&lt;/span&gt;
                &lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
	                &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Home&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
	                &lt;span class="nx"&gt;ifLoggedIn&lt;/span&gt;
	            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ifloggedIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/logout&amp;#39;&lt;/span&gt; &lt;span class="nx"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Logout&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I have not shown above (because I&amp;#8217;m trying to keep close to Duda&amp;#8217;s sample) the Turing completeness that could be worked in whether would be best or worse practice with the hypothetical markup language.&lt;/p&gt;
&lt;h3&gt;With a bootstrap for the browsers shipping today&lt;/h3&gt;
&lt;p&gt;You would client side code in this style today, but only if you&amp;#8217;ve made a bootstrapping JavaScript library that can act as parser and liaison to the inevitable &lt;span class="caps"&gt;DOM&lt;/span&gt; mutations.  We know this is possible as &lt;a href="http://cappuccino.org/"&gt;Cappuccino&lt;/a&gt; has done this for many years &amp;#8211; refer &lt;a href="http://280slides.com/"&gt;280slides&lt;/a&gt;  Note that Cappuccino is another column-of-operations style of UI markup.  That is because what it is inspired by (Objective-C) is also in that style, notwithstanding InterfaceBuilder and the resulting nib/xib files which are not human readable.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;I asked Martin Fowler what this style is called, and he says that it is &amp;#8216;Nested Function&amp;#8217; form/style.  He mentions in his &lt;span class="caps"&gt;DSL&lt;/span&gt; book, and he reminds me that it is very common in the Lisp world, and uncommon in the OO world.&lt;/p&gt;
&lt;p&gt;Incidentally, I remember a markup for an early version of InterfaceBuilder (&amp;#8216;Next&amp;#8217; in the late 80&amp;#8217;s, before Apple subsumed it) that used a form of Lisp for the format of the saved UI files.  It used Lisp for the &lt;span class="caps"&gt;WYSIWYG&lt;/span&gt; load/save cycle, as well as (I think) the run-time execution.  I&amp;#8217;ve emailed a bunch of people about that time before the format was changed to &amp;#8216;nib&amp;#8217; and nobody can corroborate, so I must have dreamed it.&lt;/p&gt;
&lt;p&gt;Folks that are interested should read about &lt;a href="http://erector.rubyforge.org/"&gt;Erector&lt;/a&gt; and &lt;a href="https://github.com/whymirror/shoes/"&gt;Shoes&lt;/a&gt;, and Duda&amp;#8217;s &lt;a href="https://github.com/dudadornelles/mjs"&gt;&lt;span class="caps"&gt;MJS&lt;/span&gt;&lt;/a&gt; and something he found that is similar &amp;#8211; &lt;a href="http://amix.dk/blog/post/19505"&gt;node.magic_dom&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Anyway, I&amp;#8217;m hoping that Google Dart stuff is nested functions, interoperating with the &lt;span class="caps"&gt;DOM&lt;/span&gt;, in the sandbox, negating the need to use any &lt;span class="caps"&gt;HTML&lt;/span&gt;, more coffee than ecma, and easy one the eye, with a bootstrap for modern browsers until native support is baked in.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/8rRGZuQEpQw" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/09/12/what-is-dart-going-to-be/</feedburner:origLink></item><item><title>Refactoring Experiment - 'least depending and most depended on' components first</title><link>http://feedproxy.google.com/~r/paulhammant/~3/uEiE-0vFUc0/</link><pubDate>Thu, 01 Sep 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/09/01/refactoring-experiment</guid><description>&lt;p&gt;&lt;strong&gt;This article follows on from &lt;a href="blog/a-spring-framework-shortcoming.html"&gt;A Spring Shortcoming&lt;/a&gt; that had a nice diagram:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/spring_injection2.png" alt="" /&gt;&lt;/p&gt;
&lt;h3&gt;The challenge&lt;/h3&gt;
&lt;p&gt;I now have a &lt;a href="https://github.com/paul-hammant/refactoring_experiment"&gt;github codebase&lt;/a&gt; that is an very slim&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt; example of that Spring app previously outlined.  The code is 100% covered, though that is by accident as you will see on investigation.  I have a challenge for folks.&lt;/p&gt;
&lt;p&gt;Context: You are a developer who has just committed a change to this codebase. Before you tell your PM that you&amp;#8217;ve finished it, you are going to embark on a refactor given you are acclimated to presently.  We all love &amp;#8220;Red, Green, Refactor&amp;#8221; &amp;#8211; right ?&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;You have seen that the controller you&amp;#8217;ve been working on has two request-mapped methods &amp;#8216;placeOrder&amp;#8217; and &amp;#8216;seeSuggestedProducts&amp;#8217; should not be in that controller class.  You want to fix both, by pushing them to new controllers  You also suspect that UpSell should be a collaborator (but still request scoped).&lt;/li&gt;
	&lt;li&gt;Using Intellij or Eclipse Refactoring operations, attack one of the two misplaced request-mapped methods first, try to move it to a new class.&lt;/li&gt;
	&lt;li&gt;Work out part way through that you should have started in the wrong place&lt;sup class="footnote"&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt; and instead should use the same techniques for &amp;#8216;findUpsell&amp;#8217; first, and made a collaborator class of that.&lt;/li&gt;
	&lt;li&gt;Flushed from that success, you should be able to go back to both of the other misplaced methods and pushed them using refactoring operations to new classes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are not going to be able to do it all with refactoring operations.  Ctrl-Z or abandoning changes in Git will be part of the workflow for the above when you confirm you have taken the wrong direction.  Cut/Paste is going to have to happen too sadly in a couple of places, as Intellij can&amp;#8217;t do all your intended moves for you.&lt;/p&gt;
&lt;p&gt;If you are willing to record and publish a video of the series of refactorings, I will link to it from this posting or a follow up.&lt;/p&gt;
&lt;h3&gt;The take away?&lt;/h3&gt;
&lt;p&gt;.. should have been do &amp;#8220;least depending and most depended on&amp;#8221; components first&lt;sup class="footnote"&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt;. Jeez, there&amp;#8217;s only one hit for that in Google. Was it mentioned in the &lt;a href="http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052"&gt;Michael Feather&amp;#8217;s book?&lt;/a&gt;&lt;/p&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Yes, the code really isn&amp;#8217;t a functional Spring-&lt;span class="caps"&gt;MVC&lt;/span&gt; app, it is an approximation of one, useful only in this context.&lt;/p&gt;
&lt;p class="footnote" id="fn2"&gt;&lt;sup&gt;2&lt;/sup&gt; &lt;a href="http://www.infoq.com/articles/drinking-your-guice-too-quickly"&gt;Refer drinking your Guice too Quickly on InfoQ&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Responses&lt;/h2&gt;
&lt;p&gt;Dec 10, 2011: Danilo Sato has has a &lt;a href="http://www.dtsato.com/blog/2011/12/10/refactoring-strategies-a-walkthrough-experiment/"&gt;blog entry&lt;/a&gt; in his series on the differences between Mechanical and Strategic refactorings, and their benefits.  He&amp;#8217;s got a &lt;a href="http://vimeo.com/33403686"&gt;14 minute video walkthrough&lt;/a&gt; of the refactorings needed as you push towards &amp;#8220;most stable&amp;#8221; code.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/uEiE-0vFUc0" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/09/01/refactoring-experiment/</feedburner:origLink></item><item><title>Contrasting Selenium 2 locator styles</title><link>http://feedproxy.google.com/~r/paulhammant/~3/3i50Uu2fXhg/</link><pubDate>Wed, 31 Aug 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/08/31/contrasting-selenium2-locator-styles</guid><description>&lt;p&gt;Just a quickie - with the Etsy.com tutorial in the JBehave we have three styles of using Selenium 2 (WebDriver) to locate and click on things in the page. The Page objects (which still need some work) illustrate the difference.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Plain Java, canonical WebDriver style&lt;/li&gt;

&lt;li&gt;Plain Java, WebDriver in fluent style&lt;/li&gt;

&lt;li&gt;Groovy + Geb&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Plain Java style:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='n'&gt;findElement&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;xpath&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;//a[@title = &amp;#39;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;section&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;&amp;#39;]&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;)).&lt;/span&gt;&lt;span class='na'&gt;click&lt;/span&gt;&lt;span class='o'&gt;();&lt;/span&gt;
&lt;span class='n'&gt;findElement&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;search-query&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;)).&lt;/span&gt;&lt;span class='na'&gt;sendKeys&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;thing&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
&lt;span class='n'&gt;WebElement&lt;/span&gt; &lt;span class='n'&gt;select&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;findElement&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;xpath&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;//select[@class =&amp;#39;handmade&amp;#39;]&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;));&lt;/span&gt;
&lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nf'&gt;Select&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;select&lt;/span&gt;&lt;span class='o'&gt;).&lt;/span&gt;&lt;span class='na'&gt;selectByValue&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;subCategory&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fluent style:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='n'&gt;link&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;xpath&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;@title = &amp;#39;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;section&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;)).&lt;/span&gt;&lt;span class='na'&gt;click&lt;/span&gt;&lt;span class='o'&gt;();&lt;/span&gt;
&lt;span class='n'&gt;input&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;search_query&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;)).&lt;/span&gt;&lt;span class='na'&gt;sendKeys&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;thing&lt;/span&gt;&lt;span class='o'&gt;).&lt;/span&gt;&lt;span class='na'&gt;submit&lt;/span&gt;&lt;span class='o'&gt;();&lt;/span&gt;
&lt;span class='n'&gt;select&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;className&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;handmade&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;)).&lt;/span&gt;&lt;span class='na'&gt;selectByValue&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;subCategory&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Groovy+Geb style:&lt;/em&gt;&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='java'&gt;&lt;span class='n'&gt;$&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='n'&gt;title&lt;/span&gt; &lt;span class='o'&gt;:&lt;/span&gt; &lt;span class='n'&gt;section&lt;/span&gt;&lt;span class='o'&gt;).&lt;/span&gt;&lt;span class='na'&gt;click&lt;/span&gt;&lt;span class='o'&gt;()&lt;/span&gt;
&lt;span class='n'&gt;$&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;#search-query&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;).&lt;/span&gt;&lt;span class='na'&gt;sendKeys&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;thing&lt;/span&gt;&lt;span class='o'&gt;)&lt;/span&gt;
&lt;span class='c1'&gt;// a small issue in geb presently makes this less elegant&lt;/span&gt;
&lt;span class='k'&gt;new&lt;/span&gt; &lt;span class='nf'&gt;Select&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;$&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;select.handmade&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;).&lt;/span&gt;&lt;span class='na'&gt;getElement&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;0&lt;/span&gt;&lt;span class='o'&gt;)).&lt;/span&gt;&lt;span class='na'&gt;selectByValue&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;subCategory&lt;/span&gt;&lt;span class='o'&gt;)&lt;/span&gt;
&lt;span class='c1'&gt;// if it were not for that issues, the code should be:&lt;/span&gt;
&lt;span class='n'&gt;$&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;form&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;).&lt;/span&gt;&lt;span class='na'&gt;category&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;subCategory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I am not sure if the fluent style is better than the canonical Java way, as the latter&amp;#8217;s locators may survive some page refactorings. I wish the WebDriver and WebElement APIs were more fluent though. Not least - I wish things didn&amp;#8217;t return void.&lt;/p&gt;

&lt;p&gt;Also, Geb&amp;#8217;s last release is against Selenium 2.0rc3. We&amp;#8217;re at 2.5.0 now, and that&amp;#8217;s too far behind for my liking. If you attempt to force an upgrade of dependencies for Geb, you will encounter subtle incompatibilities. The work around for one of those is shown in the new Select(&amp;#8230;) logic. Also, I&amp;#8217;d wish that Geb did the fluent style too - matching what the WATIR team champion more closely that the fluent selenium thing shown in #2 above.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/3i50Uu2fXhg" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/08/31/contrasting-selenium2-locator-styles/</feedburner:origLink></item><item><title>No One Gives Themselves Bad Ratings (in hiring, apparently)</title><link>http://feedproxy.google.com/~r/paulhammant/~3/Rugdg4CeKzY/</link><pubDate>Tue, 09 Aug 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/08/09/no-one-gives-themselves-bad-ratings-in-hiring</guid><description>&lt;p&gt;On the selenium-user mail list, it was hypothesized that candidates rate themselves well in respect of their skills match to the job in question. It was said &amp;#8220;No One Gives Themselves Bad Ratings&amp;#8221;.&lt;/p&gt;

&lt;p&gt;It turns out that this is not universally the case: True experts, when questioned, rate their own skills as lower than they should.&lt;/p&gt;

&lt;h2 id='the_dunning_kruger_effect'&gt;The Dunning Kruger effect&lt;/h2&gt;

&lt;p&gt;This epiphany is one of a few that comes out the research that Dunning and Kruger have done to explore long-believed tendencies within society. The definitive write-up is &lt;a href='http://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect'&gt;on Wikipedia&lt;/a&gt;, though that&amp;#8217;s not very prose-full.&lt;/p&gt;

&lt;p&gt;Easier reads are available: &lt;a href='http://opinionator.blogs.nytimes.com/2010/06/20/the-anosognosics-dilemma-1/'&gt;New York Times article from a year ago&lt;/a&gt; and a &lt;a href='http://www.psychologytoday.com/blog/evolved-primate/201006/when-ignorance-begets-confidence-the-classic-dunning-kruger-effect'&gt;Psychology today on the same timescale&lt;/a&gt;. Here is one from the turn of the millennia that I particularly like &lt;a href='http://www.sfgate.com/cgi-bin/article.cgi?file=/chronicle/archive/2000/01/18/MN73840.DTL'&gt;on the San Francisco Chronicle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The one-line take away?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The skills to recognize incompetence in others are the same as the skills to recognize incompetence in self.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The slightly longer version?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Candidates who are experts rate themselves lower (typically) than they really are and incompetent ones rate higher. The competent ones only realize the injustice of their position in the rankings, when they are confronted with the self-scores of those that they knew already were less able.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, be wary of rejecting candidates who self-rate to lower scores that a threshold you have set. &lt;em&gt;Always&lt;/em&gt; do some independent verification.&lt;/p&gt;

&lt;h2 id='steve_jobs_view_on_a_b_and_c_players'&gt;Steve Jobs&amp;#8217; view on A, B and C players&lt;/h2&gt;

&lt;p&gt;Read also some thoughts on &lt;em&gt;A players, B player and C players&lt;/em&gt; in a &lt;a href='/blog/thoughtworks-sweetspot.html#no_bar_lifting'&gt;previous blog entry&lt;/a&gt;. This in particular suggests that incompetents should be removed from hiring or you will end up lowering the bar. Steve Jobs may have been one of many that was pre-empting the findings of Dunning and Kruger later.&lt;/p&gt;

&lt;p&gt;Incidentally, I have to thank Agile industry legends &lt;a href='https://twitter.com/#!/PapaChrisMatts'&gt;Chris Matts&lt;/a&gt; and &lt;a href='https://twitter.com/#!/andy_pols'&gt;Andy Pols&lt;/a&gt; for first putting me on to this stuff in 2002.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/Rugdg4CeKzY" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/08/09/no-one-gives-themselves-bad-ratings-in-hiring/</feedburner:origLink></item><item><title>Stitz's Five port rule for enterprise application dev</title><link>http://feedproxy.google.com/~r/paulhammant/~3/1a1SWqT33pw/</link><pubDate>Sat, 06 Aug 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/08/06/five-port-rule</guid><description>&lt;h2&gt;Five ports only&lt;/h2&gt;
&lt;p&gt;At a client, ThoughtWorkers were discussing the maximum allowable ports a team should use when configuring an enterprise application they are making.  Jeremy Stitz (&lt;a href="http://www.spoke.com/info/pCZi6ca/JeremyStitz"&gt;no blog&lt;/a&gt;) held up a hand an contributed an exasperated &amp;#8220;five&amp;#8221;.  That seemed like a good, if arbitrary, number to define the simplicity we are hoping to encourage here.&lt;/p&gt;
&lt;h2&gt;The problem we&amp;#8217;re trying to solve&lt;/h2&gt;
&lt;p&gt;A production stack composed of: Multiple machines and multiple process. Each is different, though sometimes there could be duplication because one particular permutation appears to be overloaded when live. There is going to be a lot of configuration for the production stack. Sometimes that config is spread around. Sometimes in is centralized in a single properties or &lt;span class="caps"&gt;XML&lt;/span&gt; file that is supplied to all booting processes. Invariably, the configuration is different in production than it is in any of the development, testing or staging environments.  Then there are developer workstations where we should be observing developers bring up a microcosm of the stack before they commit each change, that are different again. The company in question, now needs a whole team to manage configuration. There nothing approaching simplicity or uniformity with such deployments.&lt;/p&gt;
&lt;p&gt;More than once a client in my past has managed to deploy cookie-cut zip files to a range of machines and bring them all up to serve all service needs for the tiers above.  I&amp;#8217;m going to return to this topic in future blog entries, but for now am going to concentrate on one aspect &amp;#8211; ports and the configuration thereof .&lt;/p&gt;
&lt;h2&gt;Sockets versus ports.&lt;/h2&gt;
&lt;p&gt;The enterprise app that you&amp;#8217;re making may have hundreds of deployed nodes.  Hopefully this is horizontal rather than vertical, and that you&amp;#8217;re driven by easier scaling meaning that many nodes are identical.  If you look at your whole stack in production, you may observe hundreds if not thousands of sockets concurrently open. Those could be incoming of outgoing.  Understand that we&amp;#8217;re not trying to restrict sockets here, thus you can still have your multi-process or multi-machine deployment.&lt;/p&gt;
&lt;p&gt;Be reminded that sockets are not ports.  Sockets are a software instantiation that&amp;#8217;s actually shuttling data back and forth, whereas a port is more of an intention to do something.  An app could be listening on a port, with the intention of streaming data via a socket in due course.  Similarly outgoing port usage results is sockets being allocated.  Here is a &lt;a href="http://stackoverflow.com/questions/152457/what-is-the-difference-between-a-port-and-a-socket"&gt;good stack overflow question&lt;/a&gt; on the topic. Note &amp;#8211; ports vs sockets is a fun interview question.&lt;/p&gt;
&lt;h2&gt;Ports 80 and 443, and OS level ports&lt;/h2&gt;
&lt;p&gt;Most apps we are making these days are web based.  Therefore using ports 80 and 443 is going to bring your available count down to three, if you are trying to hit the five-port rule.&lt;/p&gt;
&lt;p&gt;Your apps could well be using ports like &lt;a href="http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol"&gt;&lt;span class="caps"&gt;SNMP&lt;/span&gt;&lt;/a&gt; (ports 161, 162) and you might not know it.  We are not trying to legislate against the ports your app is inadvertently using, just the ones you state in application configuration (&lt;span class="caps"&gt;XML&lt;/span&gt;, &lt;span class="caps"&gt;YAML&lt;/span&gt;, properties files etc)&lt;/p&gt;
&lt;h2&gt;Sharing multiple services on one port&lt;/h2&gt;
&lt;p&gt;Having &amp;#8216;Zip Code service&amp;#8217; (yes that&amp;#8217;s a zipcode, here&amp;#8217;s some information about it) handled by an web-server node, that also handles authentication is easy to imagine.  They would be two different URLs on the same apparent web server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;http://myserver/zipcode
http://myserver/authentication
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Imagine you had need in one stack, to support ZipCodeService 1.0, 1.1 and 2.0 though.  These were essentially successive releases of the same thing, but you still need to support all there.  Perhaps your application&amp;#8217;s own code uses those three versions and sadly cannot be upgraded in lockstep.  Perhaps they are used by remote customers who are also not upgrading in lockstep.  What you really need to do is bake your differences into URLs (assuming &lt;span class="caps"&gt;HTTP&lt;/span&gt;) and construct a deployed process that can handle the multiplicity of that with ease.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="bash"&gt;http://myserver/zipcode/1.0
http://myserver/zipcode/1.1
http://myserver/zipcode/2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Let us assume Java or C# (.Net) for a second, and each of those three versions of the same thing has binary dependencies that are incompatible.  This is not so much the &lt;a href="http://www.google.com/search?q=diamond+dependency+problem"&gt;diamond dependency problem&lt;/a&gt;, but that is an interesting topic anyway.  With Java and .Net you cannot construct a single class path which has duplications on it, let alone incompatible duplications.  Well you can, but you should not as the results will not be what you wish for.  You are going to have to construct something that allows for incompatible jars/dlls in the same deployed app, that is more sophisticated than a simple classpath.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/classloader_tree.png" title="example classloader tree" alt="example classloader tree" /&gt;&lt;/p&gt;
&lt;p&gt;The booter is what constructs this classloader tree.  In order it makes the common classloader, the HttpHandler, using the common one as its&amp;#8217;s parent, then each of the three zip-code implementations with their respective binary dependencies in the same classloader.  Note that there&amp;#8217;s a duplicate of an individual jar (it doesn&amp;#8217;t matter really).&lt;/p&gt;
&lt;p&gt;Once constructed the booter then configures actual endpoints.  Think of that stage as more regular Java like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/zipcode/1.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ZipCodeService&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// and the others&lt;/span&gt;
&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startListening&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Except that the booter (which intends itself to be garbage collected as soon as it&amp;#8217;s completed its phase) does not see any of the child classloaders directly so will have to use reflection (please imagine the implementation of the fluent helper methods):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="java"&gt;&lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handlerClassloader&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;HttpHandler&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;zipCode1p0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipCode1p0Classloader&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ZipCodeService&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;register&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/zipcode/1.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zipCode1p0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;zipCode1p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipCode1p1Classloader&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ZipCodeService&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;register&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/zipcode/1.1&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zipCode1p1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;zipCode2p0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipCode2p0Classloader&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;make&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ZipCodeService&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;register&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/zipcode/2.0&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zipCode2p0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;startListening&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;withNoArgs&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note also in the diagram that solid lines represent the direction of class visibility.  No class in the classloader for ZipCode 1.1 can instantiate any class from its two siblings nor, any class from HttpHandler&amp;#8217;s classloader, let alone the one for the booter that should long since be garbage collected by the time the app is running.  The booter itself is a special case, as it is the one that constructs the whole classloader tree and temporarily at least has a object reference for all of the classloaders.  There are degrees of enthusiasm for hiding the implementation of things that are passed between nodes (like the request)&lt;/p&gt;
&lt;p&gt;Java servlet containers (and their capacity to host multiple &lt;span class="caps"&gt;WAR&lt;/span&gt; files) are similar, but we&amp;#8217;re defining a common &lt;span class="caps"&gt;API&lt;/span&gt; classloader here that is not itself in amongst Java&amp;#8217;s own libraries.  In fact our directed graph of classloaders here can be much larger.&lt;/p&gt;
&lt;h2&gt;Load balancing&lt;/h2&gt;
&lt;p&gt;Given we&amp;#8217;ve now got a single process that can handle everything current and legacy, we might have lost some of the scaling alleged made available from having multiple machines/processes do a single service.  The answer is simple &amp;#8211; deploy multiple instances of this general purpose services container, and use a regular load balancer to make them appear as one to their clients.&lt;/p&gt;
&lt;h2&gt;Non-web solutions&lt;/h2&gt;
&lt;p&gt;Not into RESTfulness? Into &lt;span class="caps"&gt;CORBA&lt;/span&gt; and all that?  Sure, this is where you&amp;#8217;re going to begin to approach the five ports limit of the title.  There are more problems with load-balancing, in that off the shelf solutions for that are tuned for &lt;span class="caps"&gt;HTTP&lt;/span&gt; traffic, but it should be possible engineer a solution where services are somehow named (including versions) within a shared connection of sorts.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;d much rather compose a directed graph of services that are available for a single cookie cut process deployment, and have a single port number in configuration (80 or 443), than many more ports in play, for many different fine-grained deployments that are listed in some sprawling configuration of machine names and ports.  The big-configuration design gets worse when you realize you need one machine for ZipCode service 1.0 and 1.1, but three machines to handle the load of ZipCode service 2.0.  Stuff of nightmares; Long live Stitz&amp;#8217;s five port rule. &lt;img src="http://upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Hand_parts.jpg/220px-Hand_parts.jpg" class="right" alt="" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/1a1SWqT33pw" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/08/06/five-port-rule/</feedburner:origLink></item><item><title>Hiring Selenium QA people</title><link>http://feedproxy.google.com/~r/paulhammant/~3/vAuBRH3mVaI/</link><pubDate>Thu, 04 Aug 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/08/04/hiring-selenium-quality-assurance-peope</guid><description>&lt;h2 id='mixed_skills'&gt;Mixed skills&lt;/h2&gt;

&lt;p&gt;Ideally people working with Selenium to automate the testing of applications will have some development skills in addition to their QA skills. It could be that they are an experienced developer and they are helping start a QA automation side to their employer’s project. It could be that they are a QA who has used a number of tools (like QTP) and is now trying to apply those skills to a more accessible Selenium solution. Lastly, it could be that they are a manual QA in the past and this is their first foray into QA Automation.&lt;/p&gt;

&lt;p&gt;We’re going to try to speak to all of those types in this section of SeleniumHQ. We will phase this as if it were candidate selection for interviews to join a project team.&lt;/p&gt;

&lt;h1 id='levels_of_experience'&gt;Levels of experience&lt;/h1&gt;

&lt;h2 id='inexperienced'&gt;Inexperienced&lt;/h2&gt;

&lt;p&gt;The candidate in question has not used Selenium in the work place, and may only have played with Selenium-IDE in advance of an interview (at the recruitment agent’s suggestion perhaps).&lt;/p&gt;

&lt;p&gt;They may have a resume that speaks of Quick Test Professional (QTP), Test Director, or even WinRunner from some years ago. They may alternatively have a resume that details manual testing of web applications, using or not using tools to the cataloging of manual test suites and execution of the same.&lt;/p&gt;

&lt;p&gt;When meeting these candidates, you may find programming languages, technologies, libraries, frameworks listed, but be aware that the candidate may be talking of team experience rather than personal use. In order to identify them as inexperience, you&amp;#8217;ll have to construct questions that can differentiate between team and personal experience.&lt;/p&gt;

&lt;h2 id='entry_level'&gt;Entry Level&lt;/h2&gt;

&lt;p&gt;The candidate has used Selenium-IDE for recording scripts and playback. These suites may be saved to SCM, but could have been just on a their ‘C drive’ and implicitly not shared between team-mates. The suites could have been highly evolved, relative exhaustive and relied on by the project to validate applications prior to deployment.&lt;/p&gt;

&lt;p&gt;The candidate may not qualify for Entry level Selenium skills if they have just used Selenium-IDE sporadically to aid form filling as they manually test.&lt;/p&gt;

&lt;p&gt;If you think they qualify for entry level selenium, they should be able to use it in front of you to test a site of your choosing.&lt;/p&gt;

&lt;p&gt;One the advanced side, they may be running their suites automatically from Continuous Integration tools, and have a high level of confidence and ability to move forward with confidence. Perhaps they are using Selenium-RC to execute whole sets of ‘html’ Selenium tests.&lt;/p&gt;

&lt;p&gt;(There is at least one friend of the Selenium project that has really pushed this space, and we’re not wanting to cast that dev team as beginners with Selenium here)&lt;/p&gt;

&lt;p&gt;The nature of HTML, JavaScript and CSS should be understood to a rudimentary degree.&lt;/p&gt;

&lt;p&gt;Data driving tests experience may or may not figure at this level.&lt;/p&gt;

&lt;p&gt;The candidate has knowledge of the DOM, and for selenium specifically, how XPath, CSS or by-ID selectors are used to test the application.&lt;/p&gt;

&lt;p&gt;They are also going to be able to checkout, and commit back to one of a number os SCM tools. Most likely with a UI rather than a from the command line.&lt;/p&gt;

&lt;p&gt;When meeting these candidates, they should be able to use Selenium-IDE in front of you to test a site of your choose, and work through tricky issues exposed when play-back doesn&amp;#8217;t exactly do what the record function identified. Not jus that, but they should be able to simplify the xpath expressions the recorder has made. Choose a web-site other than your corporate one for them to test.&lt;/p&gt;

&lt;h2 id='intermediate'&gt;Intermediate&lt;/h2&gt;

&lt;p&gt;On top of Entry level &amp;#8230;&lt;/p&gt;

&lt;p&gt;The candidate has transitioned to Selenium-RC or increasingly WebDriver in the workplace. They will be able to speak to the differences.&lt;/p&gt;

&lt;p&gt;They are able to program in one of Java (including Groovy &amp;amp; Scala), Ruby, Python, Perl, C# (or VB.Net), PHP. Or similar 3GL languages. They are also adept with unit-testing (JUnit, NUnit, TestNG) frameworks for their language, as well as appropriate use of assertion technologies.&lt;/p&gt;

&lt;p&gt;They may have OO knowledge, or may be beginning to understand it. Without it, they are likely to be using their language in a procedural way (which can also be fine, if its relatively clean from a purists point of view).&lt;/p&gt;

&lt;p&gt;If they are procedural (not yet OO savvy) they must be able to demonstrate looping and appropriate conditional aspects of the languages they are experienced with. If they are in the territory of OO in terms of experience, they should be able to give examples of where the facets of OO apply to Selenium test scripts.&lt;/p&gt;

&lt;p&gt;The candidate will be able to discern between alternate selector strategies to use the most suitable. They will also use Firebug (or alternatives) to help them, and perhaps also be adept with XPathChecker or XPanther, though hopefully using them far less than Firebug.&lt;/p&gt;

&lt;p&gt;That pages are loaded in increasingly strange orders (we’re looking at you Web 2.0) should not be a hurdle to the intermediate Selenium developer. They will be able to demonstrate techniques to wait for page events such that the test scripts they make do not fail when the application is slower than normal.&lt;/p&gt;

&lt;p&gt;This level of candidate should be able to respond to broken builds (we’re all using CI right?) find the issues, and liaise with the application developers to fix things.&lt;/p&gt;

&lt;p&gt;They can, most likely, arbitrate over merge conflicts with the SCM tool they are experienced with. Hopefully they are as savvy with the command line, as they are with the SCM tool’s GUI.&lt;/p&gt;

&lt;p&gt;In terms of testing style, this candidate will be able to separate happy-path tests from regression suites and project related QA.&lt;/p&gt;

&lt;p&gt;The candidate, in the interview, should be able to extend an existing Selenium-RC or WebDriver codebase one or two tests more.&lt;/p&gt;

&lt;p&gt;If the candidate has not used Selenium-RC, they may still quality for &amp;#8216;intermediate&amp;#8217; if they have developed large suites of test scripts with Selenium-IDE AND gone as far as using UI-Element and other advanced features of Selenium-IDE.&lt;/p&gt;

&lt;p&gt;When meeting these candidates, they should be able to use Selenium-RC or WebDriver to script a test that they may have started in Selenium-IDE. Be lenient on the actual nature of the build-script that kicks off their chosen high-level test class.&lt;/p&gt;

&lt;h2 id='advanced'&gt;Advanced&lt;/h2&gt;

&lt;p&gt;On top of Intermediate &amp;#8230;&lt;/p&gt;

&lt;p&gt;This candidate has more programming experience. Specifically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Closures (Groovy, Python, Ruby) should be familiar to them and they know how to make or use them in a test script, and why to use them. Groovy’s community knows these as ‘Builders’&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Functional programming constructs are likely to be known by the candidate, though they are not in position to use them.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tester-developer should be able to craft large suites from scratch and define architectures for Selenium-RC or WebDriver tests. Their code, indeed because of them the whole style of the codebase should be compositional (this over and above OO). They should be able to train any newbie to the team (from formal development, or from the QA universe) in the tools and techniques needed to grow the test-base, and keep it running.&lt;/p&gt;

&lt;p&gt;The storage of test-run results in (and otherwise use of) databases is a skill this candidate will have, as is the integration of related technologies for things that are suitable for them (like Selenium-SoapUI).&lt;/p&gt;

&lt;p&gt;Testing architectures that adhere to behavior-driven testing (Cucumber, JBehave, NBehave, Cuke4Duke, RSpec and more) may figure at this ability level. The candidate will know when to use them, and when to stick with Unit-testing type technologies.&lt;/p&gt;

&lt;p&gt;The configuration of a cloud capability for their Selenium scripts to use, should be within their skills. These could be Selenium-Grid, Sauce Labs, or BrowserMob (though these are all for different purposes).&lt;/p&gt;

&lt;p&gt;The candidate, in the interview, should be able to start a Selenium-RC or WebDriver project, write a build-script, get it all under source-control, hook it up to a pre-existing CI machine and keeps things appropriately separated within the project. Indeed, this level of QA should also be able to pass objective tests for regular developers.&lt;/p&gt;

&lt;h2 id='expert'&gt;Expert&lt;/h2&gt;

&lt;p&gt;On top of Advanced &amp;#8230;&lt;/p&gt;

&lt;p&gt;The candidate will be able to build reusable frameworks and libraries that use Selenium-RC or WebDriver that introduce real value (rather than alleged value) for the for the their team.&lt;/p&gt;

&lt;p&gt;They will be able to debug into Selenium itself and provide contributions back to the Selenium team. They are more than likely to be a master of more than three 3GLs.&lt;/p&gt;

&lt;p&gt;The candidate&amp;#8217;s name might be Simon Stewart.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/vAuBRH3mVaI" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/08/04/hiring-selenium-quality-assurance-peope/</feedburner:origLink></item><item><title>My Predictive Index</title><link>http://feedproxy.google.com/~r/paulhammant/~3/GvMOaCxOGkQ/</link><pubDate>Mon, 25 Jul 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/07/25/my-predictive-index</guid><description>&lt;p&gt;I hope I&amp;#8217;m not &lt;em&gt;over sharing&lt;/em&gt; here&lt;/p&gt;
&lt;h2&gt;ThoughtWorks&amp;#8217; Predictive Index Crash Course&lt;/h2&gt;
&lt;p&gt;ThoughtWorks has a senior fellow, Ed Zaretsky, who has a decade of Predictive Index experience under his belt.  He is currently roving around the ThoughtWorks regions running sessions for senior staff. I was lucky enough to invited to to the Dallas event a week ago.  There is a lot of value to be had from taking the two-day course from Ed, but one of the biggest take-aways for me was in the understanding of colleagues &lt;em&gt;who are not like me&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The event is preceded by a couple of tasks to complete online.  The big one is a series of multi-choice questions about you and how you see yourself that takes about 10 minutes to complete.  Ed got our results to us the night before (there were seven attendees).  &lt;a href="/files/predictive_index.pdf"&gt;Here is mine in &lt;span class="caps"&gt;PDF&lt;/span&gt; form&lt;/a&gt;.  At that stage I did not get to see anyone else&amp;#8217;s.&lt;/p&gt;
&lt;p&gt;My write up was so detailed, and accurate, that I initially suspected that Ed had it pre-written based on the advice from colleagues that had known me for a number of years.  It turns out that it solely the product of an application that processes the answers I gave to the online test.  The company behind the test must have a huge set of answers for pre-known categories of PI test-taker. Or perhaps AI or something.&lt;/p&gt;
&lt;h2&gt;Interpreting the Results&lt;/h2&gt;
&lt;p&gt;About half of the two course is spent in the interpretation of the results, categorizing ourselves, and ultimately the theory of interacting with people after knowing their type.  Here&amp;#8217;s a social styles model that is similar to the one in the accompanying materials (I can&amp;#8217;t share that scan as it is not my copyright) -&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/pi.png" title="social styles model" alt="social styles model" /&gt;&lt;/p&gt;
&lt;p&gt;My type is &amp;#8216;Expresser&amp;#8217;, though a couple of indicators suggest I am &amp;#8216;Controller&amp;#8217;.  That, or &amp;#8220;I&amp;#8217;m close to the line&amp;#8221; (I forget).  As I said reading my own writeup did not have a lot of surprises for me.  Weaknesses I already knew of (and have mechanical remediations for) were listed.  It was the ones that were not listed that was more interesting to me.  Specifically it was the strengths that were written for my fellow attendees/colleagues that I wanted to read so that I could envy them.&lt;/p&gt;
&lt;p&gt;Thus I purloined the colleague to my right&amp;#8217;s writeup.  As a group we had already gone into generalities about each of the four quadrants.  His was much more opaque to me; Like it was somehow intangible.  It turns out he was an Analyzer.  I couldn&amp;#8217;t grok the way his social model worked after the superficial read of his social style detail.  That&amp;#8217;s the point I guess; The point why we were all there.  When the fellow discovered that I&amp;#8217;d read his, he was a little put put out, until he get to read mine.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not going to drill into the types too much.  I&amp;#8217;d be doing Ed&amp;#8217;s two-day session a disservice if I did. Just suffice it to say that insights into the other&amp;#8217;s social styles was invaluable. Elsewhere on the web there are lengthier explanations of &amp;#8220;Social Styles Model&amp;#8221;, with different language for the box labels and axis, go Google for them if you insist!&lt;/p&gt;
&lt;h2&gt;Agile Epiphany&lt;/h2&gt;
&lt;p&gt;Here is something that came to me during the session. The Agile methodology that ThoughtWorks helped pioneer, is a highly effective way of making custom software for clients.  There&amp;#8217;s a lot or reasons for that, but here&amp;#8217;s a perspective born from my new understanding of social models, predictive index and all that:&lt;/p&gt;
&lt;p style="padding-left:3em;"&gt;Agile is a tool for software teams that brings Controller, Expresser, Analyzer and Cooperator closer to a uniform behavior. Perhaps a more muted or normalized set of behaviors given the wide range that would be natural.  Sure, it is a procedural methodology (time-boxed stand-ups, pairing, &lt;span class="caps"&gt;TDD&lt;/span&gt;, planning game, sustainable pace, etc), but at every turn it is silently neutralizing lead figures subconscious attempts to drag the thinking in their own behavioral direction.  The blended team behavior that Agile is best way to eliminate the chance of a death march &lt;a href="http://www.waterfall2006.com/"&gt;so characteristic of Waterfall projects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lejovarughese.wordpress.com/"&gt;Lejo&lt;/a&gt; contributes some rephrased language that works well for him when getting the point across to managers, that speaks to the same &amp;#8216;blended&amp;#8217; idea&amp;#8230;&lt;/p&gt;
&lt;p style="padding-left:3em;"&gt;&amp;#8220;waterfall processes force conformity, &lt;br /&gt;
whereas agile processes promote unity&amp;#8221;&lt;/p&gt;
&lt;h2&gt;PS &amp;#8211; Meyers-Briggs Types&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Myers-Briggs_Type_Indicator"&gt;Myers-Briggs&lt;/a&gt; defines sixteen types. We already know that people who understand their own type should not have a strategy to hire the same type, however logical that should seem.  It is a useful way to pigeon hole people, but we should understand that &lt;em&gt;all sixteen are good&lt;/em&gt; (for a balanced team and workplace).  The Myers Briggs stuff seems more applicable to interpersonal relationships, than to vocational things. Predictive Index, seems to be a much more scientific thing.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/GvMOaCxOGkQ" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/07/25/my-predictive-index/</feedburner:origLink></item><item><title>New Blog Technology and Hosting</title><link>http://feedproxy.google.com/~r/paulhammant/~3/iKITJUHJsAM/</link><pubDate>Sat, 16 Jul 2011 00:00:00 PDT</pubDate><guid isPermaLink="false">http://paulhammant.com//2011/07/16/new-blog-technology-and-hosting</guid><description>&lt;h2 id='bye_bye_subversion'&gt;Bye bye Subversion&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://git-scm.com/'&gt;Git&lt;/a&gt; is the place to be these days. I have been a happy &lt;a href='http://subversion.apache.org/'&gt;Subversion&lt;/a&gt; patron from version 0.7 onwards, but the last couple of years of Git usage have been a power-up that makes it increasing hard to use Subversion.&lt;/p&gt;

&lt;h2 id='bye_bye_sitemesh'&gt;Bye bye Sitemesh&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://www.sitemesh.org/'&gt;Sitemesh&lt;/a&gt; is about 11 years old now and at version 3, and still an incredible tool for Java web apps. It requires a constantly running JVM though which is not cheap. There have been to make it work offline like &lt;a href='http://xsite.codehaus.org/'&gt;XSite&lt;/a&gt;. &lt;a href='https://github.com/mojombo/jekyll'&gt;Jekyll&lt;/a&gt; has been made for static site solutions and it is all that I need. Chatting to &lt;a href='http://joewalnes.com/'&gt;Joe Walnes&lt;/a&gt; (the architect of Sitemesh), I found out that he likes the same tool-chain outlined here for his static sites.&lt;/p&gt;

&lt;p&gt;Interestingly, &lt;a href='http://tom.preston-werner.com/'&gt;Tom Preston-Warner&lt;/a&gt; suggests that Jekyll &amp;#8216;was primarily inspired by a Ruby project called Webby&amp;#8217;, and that he himself had not heard of Sitemesh at the time of Jekyll&amp;#8217;s creation. It is funny how things go; Long gone are the days that it was possible to know everything that is relevant in the world of software development.&lt;/p&gt;

&lt;h2 id='bye_bye_kompozer_and_wysiwyg_html_editing'&gt;Bye bye KompoZer (and WYSIWYG HTML editing)&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://www.textism.com/tools/textile/'&gt;Textile&lt;/a&gt; and &lt;a href='http://daringfireball.net/projects/markdown/'&gt;Markdown&lt;/a&gt; are nearly as good as a WYSIWYG editor, for the type of publishing I am concerned with. Though I&amp;#8217;m a Junior committer for &lt;a href='http://kompozer.net/'&gt;KompoZer on SourceForge&lt;/a&gt;, I am not going to use it for blog editing any more.&lt;/p&gt;

&lt;h2 id='hello_githubpages'&gt;Hello Github-pages&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://pages.github.com/'&gt;Github pages&lt;/a&gt; is incredible. I have been a fan for a year now, and &lt;a href='github-as-a-cms-to-end-cmses.html'&gt;my last blog entry was about it&lt;/a&gt;. I have managed to preserve all my old article in the migration. I did a git-svn checkout (keeping history is important to me) and a git-push into Github. All my old URLs work, though the ATOM feed has changed. Too many people botch their blog conversions totally (I am looking at you &lt;a href='http://www.lixo.org/'&gt;Carlos Villela&lt;/a&gt;), or lamely supply a forwarding link.&lt;/p&gt;

&lt;p&gt;For twenty or so blog entries, I ran &lt;a href='http://aftnn.org/content/code/html2textile/'&gt;html2textile&lt;/a&gt; to make textile. It was not perfect, as the in-line code examples gets chewed up, and I have had to preprocess my HTML with some perl-one-liners to remove evidence my apparent love of &amp;#38;nbsp; I have cloned &lt;a href='http://minimum.layouts-the.me/'&gt;a minimal layout&lt;/a&gt; by Yuya Saito, and dropped much of the bling that my old Sitemesh decorator added.&lt;/p&gt;

&lt;h2 id='old_blog_entries'&gt;Old Blog entries&lt;/h2&gt;

&lt;p&gt;They are preserved &lt;a href='/blog/index.html'&gt;here&lt;/a&gt;. Google will index them, and otherwise preserve their pagerank, but they have disappeared from my Atom feed now.&lt;/p&gt;

&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Of course thousands of folks before me have settled on github-pages, so I am not trying to highlight anything new here. I think that anyone technical should look to this platform for hosting of mixed reference and blog materials, including project documentation, small company web sites and where Wiki or CMS solutions might have been used in the past.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/paulhammant/~4/iKITJUHJsAM" height="1" width="1"/&gt;</description><feedburner:origLink>http://paulhammant.com//2011/07/16/new-blog-technology-and-hosting/</feedburner:origLink></item></channel></rss>

