<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 <title>Alex Young</title>
 
 <link href="http://alexyoung.org" />
 <updated>2009-11-09T19:43:40+00:00</updated>
 <id>http://alexyoung.org/</id>
 <author>
   <name>Alex Young</name>
   <email>a@alexyoung.org</email>
 </author>
 
 <link rel="self" href="http://feeds.feedburner.com/webdevelopernotes" type="application/atom+xml" /><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
   <title>Riotjs: A JavaScript Unit Testing Framework</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/y-ka8M6Bcwk/riotjs" />
   <updated>2009-11-04T00:00:00+00:00</updated>
   <id>http://alexyoung.org/2009/11/04/riotjs</id>
   <content type="html">&lt;p&gt;I really like &lt;a href="http://alexyoung.org/2009/10/26/riot-testing/"&gt;Riot&lt;/a&gt;, the Ruby unit testing framework, so I decided to port it to JavaScript.  You can get it from GitHub at &lt;a href="http://github.com/alexyoung/riotjs"&gt;alexyoung/riotjs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve worked hard to keep the syntax minimal.  This is challenging in JavaScript, as you may have gathered from my &lt;a href="http://alexyoung.org/2009/10/22/javascript-dsl/"&gt;Fear and Loathing in JavaScript DSLs&lt;/a&gt; article.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s actually no need for a JavaScript &amp;#8220;&lt;span class="caps"&gt;DSL&lt;/span&gt;&amp;#8221; in this case &amp;#8212; riotjs could work fine by passing around objects.  It&amp;#8217;s just that riot tests are so terse that I wanted to carry this over to JavaScript.  Up until now I&amp;#8217;ve been using unittest.js from &lt;a href="http://script.aculo.us/"&gt;script.aculo.us&lt;/a&gt; and I always felt the tests were too messy.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a few interesting things to note about it:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Like cucumber the output is verbose&lt;/li&gt;
	&lt;li&gt;It&amp;#8217;ll work with Rhino, I do a lot of work this way because it&amp;#8217;s very fast and focused&lt;/li&gt;
	&lt;li&gt;I colourize the output in the terminal&lt;/li&gt;
	&lt;li&gt;Your tests will also run in a browser without any modification&lt;/li&gt;
	&lt;li&gt;There are some aliases: &lt;code&gt;should&lt;/code&gt; is aliased to &lt;code&gt;asserts&lt;/code&gt;, and &lt;code&gt;given&lt;/code&gt; is aliased as &lt;code&gt;context&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;Those methods &lt;strong&gt;are not&lt;/strong&gt; global because I&amp;#8217;m a cunning magician&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#8217;s an example test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test_helper.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;Riot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Functions&amp;#39;&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="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a function to curry&amp;#39;&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;add&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="nx"&gt;a&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="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&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="o"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;add1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Closure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;add&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;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;return expected values&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;add1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a function to memoize&amp;#39;&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fibo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Closure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&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="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="nx"&gt;recur&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&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;recur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;recur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;return fibonacci numbers&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fibo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;55&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;Riot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This test is from my &lt;a href="http://github.com/alexyoung/closure"&gt;closure&lt;/a&gt; library, which is a bunch of functional stuff (with lazy evaluation!) for JavaScript.  Here&amp;#8217;s what the output looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/riotjs.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I could also include that script in &lt;span class="caps"&gt;HTML&lt;/span&gt;, because &lt;code&gt;Riot.run&lt;/code&gt; will automatically run the tests &lt;code&gt;onload&lt;/code&gt;.  Additionally, &lt;code&gt;Riot.run&lt;/code&gt; can be passed a function containing the tests, rather than having a call to run.  The reason I call run here is because I have a set of files I want to run to test the entire libraries.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t be afraid to message me about riotjs on GitHub, it&amp;#8217;s a work in progress so I&amp;#8217;d appreciate contributions and thoughts.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=y-ka8M6Bcwk:9uyv9-gMr9o:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=y-ka8M6Bcwk:9uyv9-gMr9o:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=y-ka8M6Bcwk:9uyv9-gMr9o:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/y-ka8M6Bcwk" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/11/04/riotjs</feedburner:origLink></entry>
 
 <entry>
   <title>Running a Web Business in the UK</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/TmQsjifXggM/running-a-tech-business" />
   <updated>2009-10-29T00:00:00+00:00</updated>
   <id>http://alexyoung.org/2009/10/29/running-a-tech-business</id>
   <content type="html">&lt;p&gt;A friend of mine shared this link with me: &lt;a href="http://eu.techcrunch.com/2009/10/29/startup-school-2009-37-signals-has-some-lessons-for-european-startups/"&gt;37signals has some lessons for European startups&lt;/a&gt;.  It made me think about 37signals business and marketing approach to web apps &amp;#8212; what works and what doesn&amp;#8217;t for the rest of us.&lt;/p&gt;
&lt;p&gt;I build web applications and I also freelance.  My company is called Helicoid.  I&amp;#8217;ve found that marketing software without a good chunk of capital is like playing the lottery.  There&amp;#8217;s a chance that your apps might get noticed, but you need to sustain interest over time.  You might have noticed how often certain businesses appear on premium advertising services like &lt;a href="http://decknetwork.net/"&gt;The Deck&lt;/a&gt; &amp;#8212; that&amp;#8217;s because they invest a serious amount of money into marketing.&lt;/p&gt;
&lt;p&gt;Why do some businesses have to pay thousands of dollars to appear on The Deck while 37signals don&amp;#8217;t?  The secret is community.&lt;/p&gt;
&lt;h3&gt;History and Community&lt;/h3&gt;
&lt;p&gt;37signals started as a design agency.  They had some great public-facing projects: single page redesigns, a design-oriented blog, and a book called &lt;a href="http://www.amazon.com/Defensive-Design-Web-improve-messages/dp/073571410X"&gt;Defensive Design&lt;/a&gt;.  All of these things conspired to make their blog incredibly popular.  Meanwhile, their work became increasing technical, and Basecamp evolved from the desire to better communicate with clients.&lt;/p&gt;
&lt;p&gt;Once they hit upon the idea of selling Basecamp as a web application, they had thousands of readers on their blog to sell it to.  This is a marketing dream come true.  There&amp;#8217;s literally no way you can emulate this.  You can&amp;#8217;t buy thousands of people and make them follow your work and trust you.&lt;/p&gt;
&lt;p&gt;With this history in mind, lots of 37signals&amp;#8217; business advice makes sense, &lt;strong&gt;within the context of their business&lt;/strong&gt;.  Unless you run a community or have a popular blog, you&amp;#8217;re going to need to &lt;strong&gt;think for yourself&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you have access to such a community then you&amp;#8217;re in an incredible position: &lt;strong&gt;selling to hundreds of people who are genuinely interested in what you do is the best marketing strategy in the world&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Fundamental Business Concepts&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;re interested in forming a business, it&amp;#8217;s worth learning what &lt;em&gt;limited&lt;/em&gt; in limited company means.  It&amp;#8217;s important to understand that the liability is limited to the company itself, not you.  That means the risk of failure isn&amp;#8217;t as drastic as it sounds, as long as you run your business legally.&lt;/p&gt;
&lt;p&gt;Running a business legally is made easier by finding a good accountant.  I&amp;#8217;m not talking about someone who will find legal loopholes in your country&amp;#8217;s tax system, I&amp;#8217;m talking about someone who will give you tax and even business advice.&lt;/p&gt;
&lt;p&gt;My accountant helped with our company&amp;#8217;s formation, and without him I&amp;#8217;d never have got started.  I found him with Google by searching for accountants in my area.&lt;/p&gt;
&lt;h3&gt;Networking and Events&lt;/h3&gt;
&lt;p&gt;Think carefully about the difference between networking, events and marketing.&lt;/p&gt;
&lt;p&gt;I go to a lot of web technology events to learn.  When I started out, I mistakenly thought these would be good for marketing.  They&amp;#8217;re terrible for marketing products unless they directly relate to the audience.&lt;/p&gt;
&lt;p&gt;Startup events are useless for product marketing.  Drinking a few beers with a group of people who are all running web technology companies isn&amp;#8217;t going to help you find customers, but it might help you find freelance work.&lt;/p&gt;
&lt;h3&gt;What Works for Me&lt;/h3&gt;
&lt;p&gt;Freelance, open source, social media and Alan Sugar.&lt;/p&gt;
&lt;p&gt;Freelancing allows me to work on my terms.  Sadly the British government has created a climate that is hostile to freelancers and contractors.  We&amp;#8217;re taxed so much you could probably run &lt;span class="caps"&gt;NASA&lt;/span&gt; off us.  To be efficient with the company&amp;#8217;s money, I invest in my web applications.&lt;/p&gt;
&lt;p&gt;I currently have over 30 GitHub projects.  A few of them have corresponding web sites, like &lt;a href="http://jschat.org/"&gt;JsChat&lt;/a&gt;.  JsChat has a reference to my company&amp;#8217;s name, so my open source efforts get paid back in a small way by people discovering my company.&lt;/p&gt;
&lt;p&gt;Social media has given me more marketing exposure than thousands of pounds of web adverts.  One of my products gets talked about all over the web on forums, Facebook, Twitter and blogs.&lt;/p&gt;
&lt;p&gt;At first I didn&amp;#8217;t like to contact blogs about my products because I felt like I was spamming them, but you really need to do this.  I launched something last year, fired off a few friendly emails to Mashable, Read Write Web and similar sites, and I had 6000 signups within two weeks.  These blogs have a needle/haystack issue, so one trick I used was to offer their readers a code to get a special premium version of my service.&lt;/p&gt;
&lt;p&gt;Producing your own content is a good marketing strategy.  Aim to write and share useful things with your audience.  I write design and technical articles here and on one of Helicoid&amp;#8217;s blogs.  The secret to social media is rhythm &amp;#8212; whenever I let the blogs slide for a few weeks it gets harder and harder to regain interest in them.  It takes a while to pay off though, you can blog for months before picking up interest.&lt;/p&gt;
&lt;p&gt;I mentioned Alan Sugar for a good reason.  I don&amp;#8217;t watch The Apprentice and I&amp;#8217;ve only seen a few episodes of Dragon&amp;#8217;s Den, and I don&amp;#8217;t think much of Amstrad.  But a few years ago I saw him on TV and he was explaining managing a business to a failed contestant.  He said he&amp;#8217;d know the exact figures for his potential market, the overheads, the marketing investment, the forecast for the next month or even year.&lt;/p&gt;
&lt;p&gt;This was a wake up call for me.  I&amp;#8217;m a programmer, and business is something I do when I&amp;#8217;m not programming.  I had to turn this mentality around to have any hope of making money selling software.&lt;/p&gt;
&lt;p&gt;Be exacting: detailed monitoring of your web apps can funnel straight back into your marketing investment.  I do this my way &amp;#8212; I have rake tasks that go off and query my apps to give me figures for various statistics.&lt;/p&gt;
&lt;h3&gt;Choose Your Own Adventure&lt;/h3&gt;
&lt;p&gt;All this advice comes down to finding what works for you, and working hard.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you build it, they won&amp;#8217;t come&lt;/strong&gt; &amp;#8212; you need to work to get people interested in your products, and you need to work to support your hard won customers.  If you don&amp;#8217;t love it you won&amp;#8217;t succeed, and if you expect to be successful without marketing you will fail.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=TmQsjifXggM:pVKEx-3jAp0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=TmQsjifXggM:pVKEx-3jAp0:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=TmQsjifXggM:pVKEx-3jAp0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/TmQsjifXggM" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/10/29/running-a-tech-business</feedburner:origLink></entry>
 
 <entry>
   <title>Riot: Ruby Unit Testing</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/NOP6uuhs7C8/riot-testing" />
   <updated>2009-10-26T00:00:00+00:00</updated>
   <id>http://alexyoung.org/2009/10/26/riot-testing</id>
   <content type="html">&lt;p&gt;&lt;a href="http://github.com/thumblemonks/riot"&gt;Riot&lt;/a&gt; is a Ruby unit testing framework.  It results in terse and expressive unit tests.  It strikes the perfect balance between shoulda and rspec-like test frameworks.  It&amp;#8217;s also very fast.&lt;/p&gt;
&lt;p&gt;Riot isn&amp;#8217;t based on &lt;code&gt;Test::Unit&lt;/code&gt; (unlike Shoulda).  It flattens your tests into contexts with sets of assertions.  It has a &lt;code&gt;setup&lt;/code&gt; block that runs before the assertions in a context.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a movement within the Ruby community to write tests with one assertion per test block.  Riot fundamentally works this way because assertions are the test block, so cheating isn&amp;#8217;t possible.  This makes for incredibly focused unit tests.&lt;/p&gt;
&lt;p&gt;Riot is still under active development by its author, &lt;a href="http://github.com/thumblemonks/riot/blob/master/README.markdown"&gt;Justin Knowlden&lt;/a&gt;, but it&amp;#8217;s completely usable right now.  I was so impressed by it that I&amp;#8217;ve created a JavaScript port called &lt;a href="http://github.com/alexyoung/riotjs"&gt;riotjs&lt;/a&gt; &amp;#8212; I&amp;#8217;ll cover this in another post soon.&lt;/p&gt;
&lt;h3&gt;Redundancy&lt;/h3&gt;
&lt;p&gt;Used as the author intended, Riot removes redundancy even when compared to Shoulda.&lt;/p&gt;
&lt;p&gt;Test::Unit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Unit&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_email_address_is_nil&lt;/span&gt;
    &lt;span class="n"&gt;assert_nil&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Shoulda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserTest&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Unit&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;have nil email&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;assert_nil&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Riot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;a new user&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;asserts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;email address&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;&lt;span class="caps"&gt;FAQ&lt;/span&gt; and More&lt;/h3&gt;
&lt;p&gt;Riot&amp;#8217;s author has anticipated the typical set of Ruby test tool questions in the &lt;a href="http://github.com/thumblemonks/riot/blob/master/README.markdown"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=NOP6uuhs7C8:ioGnVcu4xAI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=NOP6uuhs7C8:ioGnVcu4xAI:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=NOP6uuhs7C8:ioGnVcu4xAI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/NOP6uuhs7C8" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/10/26/riot-testing</feedburner:origLink></entry>
 
 <entry>
   <title>Fear and Loathing in JavaScript DSLs</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/0UT3v20zSlY/javascript-dsl" />
   <updated>2009-10-22T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/10/22/javascript-dsl</id>
   <content type="html">&lt;p&gt;I wanted to create an &lt;span class="caps"&gt;API&lt;/span&gt; in JavaScript that behaved like a &lt;span class="caps"&gt;DSL&lt;/span&gt;.  The aim was to cut down on unnecessary syntax in the client&lt;sup class="footnote"&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt; code.  I explored a few techniques which I will present below.  In my opinion the best technique is the final one, which my friend &lt;a href="http://annealer.org/"&gt;Annealer&lt;/a&gt; came up with.  He intimated that I co-created it, but he was just being nice so don&amp;#8217;t believe him.&lt;/p&gt;
&lt;p&gt;These examples use a fictional &lt;span class="caps"&gt;DSL&lt;/span&gt; object called &lt;code&gt;DSLRunner&lt;/code&gt; that is capable of executing the &lt;span class="caps"&gt;DSL&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Download the examples&lt;/strong&gt; here: &lt;a href="http://github.com/alexyoung/javascript-dsl-examples"&gt;javascript-dsl-examples&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Prefixed Property Names&lt;/h3&gt;
&lt;p&gt;This technique is similar to how &lt;code&gt;Test::Unit&lt;/code&gt; works in Ruby, and it&amp;#8217;s also how &lt;a href="http://script.aculo.us"&gt;script.aculo.us&lt;/a&gt; unit tests work.  Each method is prefixed with a significant name and presented to the &lt;span class="caps"&gt;DSL&lt;/span&gt; object as a list.&lt;/p&gt;
&lt;p&gt;In the following example, &lt;code&gt;first&lt;/code&gt; and &lt;code&gt;last&lt;/code&gt; will be executed before and after the set of the methods prefixed with &lt;code&gt;bake&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;print&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;print&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;;&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;DSLRunner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;run&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="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;)&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;ingredients&lt;/span&gt; &lt;span class="o"&gt;=&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;methods&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="o"&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;executeAndRemove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;first&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&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;key&lt;/span&gt; &lt;span class="k"&gt;in&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;methods&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;key&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;last&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^bake/&lt;/span&gt;&lt;span class="p"&gt;))&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;executeAndRemove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executeAndRemove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;last&amp;#39;&lt;/span&gt;&lt;span class="p"&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;addIngredient&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="nx"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;)&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;ingredients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ingredient&lt;/span&gt;&lt;span class="p"&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;executeAndRemove&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="nx"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;)&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;output&lt;/span&gt; &lt;span class="o"&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;methods&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;]();&lt;/span&gt;
    &lt;span class="nx"&gt;delete&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;methods&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;methodName&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;output&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;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;first&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I happen first&amp;quot;&lt;/span&gt;&lt;span class="p"&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;bakeCake&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Commencing cake baking&amp;quot;&lt;/span&gt;&lt;span class="p"&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;bakeBread&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Baking bread&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;/pre&gt;
&lt;/div&gt;&lt;p&gt;The problem with this example is what we&amp;#8217;ve always found with &lt;code&gt;Test::Unit&lt;/code&gt; &amp;#8212; the method names become unwieldy.  It would be nicer to be able to just &lt;code&gt;bake()&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;DSL&lt;/span&gt; Methods as Arguments to Closures&lt;/h3&gt;
&lt;p&gt;Instead of a property list, a function can be used to store the body of the code.  Passing this function methods for the &lt;span class="caps"&gt;DSL&lt;/span&gt; makes the &lt;span class="caps"&gt;DSL&lt;/span&gt; calls more readable.  Each of the &lt;span class="caps"&gt;DSL&lt;/span&gt; calls is really a method that adds callbacks to a list then executes them later.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;print&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;print&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;;&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;DSLRunner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;methodQueue&lt;/span&gt;&lt;span class="o"&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;run&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="nx"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&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;bake&lt;/span&gt;&lt;span class="o"&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;first&lt;/span&gt;&lt;span class="o"&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;last&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="k"&gt;typeof&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;firstMethod&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&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;firstMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&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="o"&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;methodQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="o"&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="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;methodQueue&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&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;lastMethod&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&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;lastMethod&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="o"&gt;,&lt;/span&gt;

  &lt;span class="nx"&gt;bake&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;methodQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&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;first&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="o"&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;last&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&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;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&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;bake&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;first&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="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I happen first&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;bake&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="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Baking bread&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;bake&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="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Baking a cake&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;/pre&gt;
&lt;/div&gt;&lt;p&gt;The implementation here can be very simple, and the &lt;span class="caps"&gt;DSL&lt;/span&gt; itself is more expressive than before.  The downside, however, is the client code has to comply to the top-level function signature (but not necessarily arity thanks to JavaScript).  Connascence of position, as Jim Weirich might say.&lt;/p&gt;
&lt;h3&gt;&lt;span class="caps"&gt;DSL&lt;/span&gt; Methods Bound Using Closures and with()&lt;/h3&gt;
&lt;p&gt;This technique binds the body against the &lt;span class="caps"&gt;DSL&lt;/span&gt; library.  It&amp;#8217;s not enough to wrap the execution of the callback inside a &lt;code&gt;with&lt;/code&gt;, metaprogramming is required.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;d like to reproduce this trick, remember that:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The function body either needs to be extracted or executed within a closure&lt;/li&gt;
	&lt;li&gt;You&amp;#8217;ll need to eval() the resulting code&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;print&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;undefined&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;print&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="o"&gt;;&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;DSLRunner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ingredients&lt;/span&gt;&lt;span class="o"&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;prepareFunctionBody&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="nx"&gt;fn&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="s1"&gt;&amp;#39;(&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\s+$/&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="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;,&lt;/span&gt;

  &lt;span class="nx"&gt;withThis&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&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;body&lt;/span&gt; &lt;span class="o"&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;prepareFunctionBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&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;return&lt;/span&gt; &lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;with(that) { &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;body&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="p"&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;run&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="nx"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;)&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;withThis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
    &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Your specified ingredients included: &amp;quot;&lt;/span&gt; &lt;span class="o"&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;ingredients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&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="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;

  &lt;span class="nx"&gt;bake&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&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="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;

  &lt;span class="nx"&gt;addIngredient&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="nx"&gt;ingredient&lt;/span&gt;&lt;span class="p"&gt;)&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;ingredients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ingredient&lt;/span&gt;&lt;span class="p"&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;last&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&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="nx"&gt;bake&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="nx"&gt;addIngredient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;flour&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;addIngredient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;yeast&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;addIngredient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;water&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Baking bread&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;bake&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="nx"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Baking a cake&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;/pre&gt;
&lt;/div&gt;&lt;p&gt;The magic here is &lt;code&gt;withThis&lt;/code&gt;.  It modifies the client&amp;#8217;s top-level function to make it automatically execute.  Another technique that can be used is to remove the function definition &amp;#8212; this requires a more complicated regex.&lt;/p&gt;
&lt;p&gt;Next, &lt;code&gt;withThis&lt;/code&gt; retains a reference to the executing class using a closure, and uses &lt;code&gt;with&lt;/code&gt; to make references to the &lt;span class="caps"&gt;DSL&lt;/span&gt; methods implicit, and calls &lt;code&gt;eval&lt;/code&gt; when ready.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;addIngredient&lt;/code&gt; method calls could be executed within a &lt;code&gt;Bake&lt;/code&gt; class that &lt;code&gt;withThis&lt;/code&gt; binds to instead.  In my real code I had &lt;code&gt;withThat&lt;/code&gt; which takes a parameter instead of using &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;A Closure and Injected &lt;span class="caps"&gt;DSL&lt;/span&gt; Methods&lt;/h3&gt;
&lt;p&gt;This technique is the one I settled on for my library.  It&amp;#8217;s a combination of the previous two techniques, without the &lt;code&gt;with()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The function body is prepared and placed in a closure, and then it&amp;#8217;s evaluated inside a function with the &lt;span class="caps"&gt;DSL&lt;/span&gt; methods passed as arguments.  In my code I call it &lt;code&gt;withDSL&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nx"&gt;withDSL&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="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&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;body&lt;/span&gt; &lt;span class="o"&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;functionBody&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;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;(function(bake, addIngredient) { &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;body&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;;&lt;/span&gt;
  &lt;span class="k"&gt;return&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bake&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addIngredient&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nb"&gt;eval&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;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DSLRunner&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&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;/pre&gt;
&lt;/div&gt;&lt;p&gt;My real code uses an optional second parameter which refers to an object that has context-sensitive methods.  This could be used to bind &lt;code&gt;addIngredient&lt;/code&gt; calls to a &lt;code&gt;Bake&lt;/code&gt; object &amp;#8212; the top-level &lt;code&gt;bake&lt;/code&gt; method would return these objects and they&amp;#8217;d include the &lt;code&gt;addIngredient&lt;/code&gt; method.&lt;/p&gt;
&lt;h3&gt;Injected &lt;span class="caps"&gt;DSL&lt;/span&gt; Methods Using Function Constructors&lt;/h3&gt;
&lt;p&gt;In this case, an alternative to &lt;code&gt;eval&lt;/code&gt; is &lt;code&gt;new Function&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;  &lt;span class="nx"&gt;withDSL&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&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;body&lt;/span&gt; &lt;span class="o"&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;prepareFunctionBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;f&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bake&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;addIngredient&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&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;bake&lt;/span&gt;&lt;span class="o"&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;addIngredient&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&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="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&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;/pre&gt;
&lt;/div&gt;&lt;p&gt;This ends up relying more on arrays and less on strings, so it uses less code than the previous method.&lt;/p&gt;
&lt;h3&gt;Phew&lt;/h3&gt;
&lt;p&gt;Making client-friendly JavaScript DSLs isn&amp;#8217;t impossible.  You might look at &lt;code&gt;with&lt;/code&gt; or &lt;code&gt;eval&lt;/code&gt; and decide these techniques aren&amp;#8217;t for you, but even meta-programming in Ruby can require a dose of analogous methods.&lt;/p&gt;
&lt;p&gt;During the evolution of my library I was constantly benchmarking, and for casual use none of these techniques added overheads (I had 1-3 millisecond drifts).&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a tip: get Rhino set up when you&amp;#8217;re exploring JavaScript.  &lt;a href="http://www.mozilla.org/rhino/"&gt;Get the Jars&lt;/a&gt; and put them in your class path and add a nice alias:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;js&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;java org.mozilla.javascript.tools.shell.Main&amp;#39;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;hr /&gt;
&lt;p class="footnote" id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; The term &amp;#8220;client code&amp;#8221; is used to refer to code written with the &lt;span class="caps"&gt;DSL&lt;/span&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=0UT3v20zSlY:ORFLSMW7inI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=0UT3v20zSlY:ORFLSMW7inI:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=0UT3v20zSlY:ORFLSMW7inI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/0UT3v20zSlY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/10/22/javascript-dsl</feedburner:origLink></entry>
 
 <entry>
   <title>BBC Glow: Overview and Example</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/WQkpxTDd9YY/bbc-glow" />
   <updated>2009-10-19T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/10/19/bbc-glow</id>
   <content type="html">&lt;p&gt;&lt;a href="http://www.bbc.co.uk/glow/"&gt;Glow&lt;/a&gt; is the BBC&amp;#8217;s JavaScript framework.  Things to note:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The namespacing makes it play nice with other frameworks (and itself)&lt;/li&gt;
	&lt;li&gt;It&amp;#8217;s split into modules that are mostly self-contained (an exception will alert you to a missing dependency but this is rare)&lt;/li&gt;
	&lt;li&gt;Modules include: dom manipulation, language extensions, events, animations&lt;/li&gt;
	&lt;li&gt;Also features UI widgets with bundled images, &lt;span class="caps"&gt;HTML&lt;/span&gt; and &lt;span class="caps"&gt;CSS&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;The homepage works with the Konami code&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Why does Glow exist?&lt;/h3&gt;
&lt;p&gt;To read what the developers say, see &lt;a href="http://www.bbc.co.uk/glow/docs/articles/what_is_glow.shtml"&gt;What is Glow?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To summarise, the &lt;span class="caps"&gt;BBC&lt;/span&gt; have strict browser support guidelines.  They also have disparate teams that can work on different parts of the same page.  Therefore:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Glow has impressive browser support: &lt;a href="http://www.bbc.co.uk/guidelines/futuremedia/technical/browser_support.shtml#support_table"&gt;support table&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Glow can work alongside different versions of itself&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;span class="caps"&gt;DOM&lt;/span&gt; and selector support&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s not as advanced as &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt; or &lt;a href="http://sizzlejs.com/"&gt;Sizzle&lt;/a&gt;, but the selectors provide enough abstraction for most tasks (child and grouping are present but pseudo selectors aren&amp;#8217;t).  Access the &lt;span class="caps"&gt;DOM&lt;/span&gt; using &lt;code&gt;glow.dom.get("selector")&lt;/code&gt;.  This will return a nodeList.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When working with Glow it&amp;#8217;s best to think in terms of node lists&lt;/strong&gt; (in a similar fashion to jQuery).  &lt;code&gt;NodeList&lt;/code&gt; has a lot of convenience methods.  Adding a &lt;span class="caps"&gt;CSS&lt;/span&gt; class or iterating over lists of nodes is easy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nx"&gt;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;img&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;filter&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;i&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#login a&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;highlight&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;Language and Data&lt;/h3&gt;
&lt;p&gt;There are a few important methods in &lt;code&gt;glow.lang&lt;/code&gt; to be aware of:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;extend&lt;/code&gt; copies the prototype from one object to another&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;interpolate&lt;/code&gt; provides simple string templates&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;map&lt;/code&gt; is useful if you like iterators and expect them to work in IE&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#8217;s an interpolate example in which I add it to &lt;code&gt;String&lt;/code&gt;&amp;#8217;s prototype for convenience:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interpolate&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="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;http://twitter.com/{from_user}&amp;quot;&amp;gt;&amp;lt;img width=&amp;quot;48&amp;quot; height=&amp;quot;48&amp;quot; src=&amp;quot;{image}&amp;quot; /&amp;gt;{from_user}&amp;lt;/a&amp;gt; {text}&amp;lt;br clear=&amp;quot;both&amp;quot; /&amp;gt;&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;profile_image_url&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;from_user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;from_user&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;text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;text&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;Net (Ajax)&lt;/h3&gt;
&lt;p&gt;The network (Ajax) &lt;span class="caps"&gt;API&lt;/span&gt; is clean and easy to use.  It&amp;#8217;s all in &lt;a href="http://www.bbc.co.uk/glow/docs/1.7/api/glow.net.shtml"&gt;glow.net&lt;/a&gt;, but there&amp;#8217;s not much to it: &lt;code&gt;glow.net.get&lt;/code&gt; and &lt;code&gt;glow.net.post&lt;/code&gt; work in a similar way to most libraries.&lt;/p&gt;
&lt;p&gt;Cross-domain requests are made possible with &lt;code&gt;glow.net.loadScript&lt;/code&gt;.  This inserts &lt;code&gt;script&lt;/code&gt; tags into the document &amp;#8212; this works especially well with &lt;span class="caps"&gt;JSON&lt;/span&gt; (pay attention to the use of &lt;code&gt;{callback}&lt;/code&gt; in this example):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nx"&gt;TwitterSearch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTrends&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://search.twitter.com/trends/current.json?callback={callback}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadScript&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;onLoad&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&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;trends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trends&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;trends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trends&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;trend&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;trend&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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;Animations&lt;/h3&gt;
&lt;p&gt;The animation &lt;span class="caps"&gt;API&lt;/span&gt; is similar to scriptaculous: it provides a set of faders, sliders, highlight and &lt;span class="caps"&gt;CSS&lt;/span&gt; mutation.  These are known as &lt;a href="http://www.bbc.co.uk/glow/docs/1.7/api/glow.anim.shtml"&gt;animation helpers&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nx"&gt;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fadeIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet_html&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;glow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bottom&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;to&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="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The other major parts of the animation &lt;span class="caps"&gt;API&lt;/span&gt; are &lt;code&gt;glow.anim.Animation&lt;/code&gt;, &lt;code&gt;glow.tweens&lt;/code&gt;, and &lt;code&gt;glow.anim.Timeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;glow.anim.Animation&lt;/code&gt; class can be used to build your own animation primitives.  Specify a duration and a tween, then add a listener for &lt;code&gt;frame&lt;/code&gt; events on the animation object itself.&lt;/p&gt;
&lt;p&gt;Tweens are functions for modifying animations &amp;#8212; there is a comprehensive set in &lt;a href="http://www.bbc.co.uk/glow/docs/1.7/api/glow.tweens.shtml"&gt;glow.tweens&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sets of animations can be orchestrated using &lt;code&gt;glow.anim.Timeline&lt;/code&gt; and an array of animations and delays (known as &lt;code&gt;channels&lt;/code&gt;).  There&amp;#8217;s a good &lt;a href="http://www.bbc.co.uk/glow/docs/1.7/furtherinfo/animtimeline/"&gt;Mexican wave example&lt;/a&gt; in the documentation.&lt;/p&gt;
&lt;h3&gt;My Glow example: Twitter search panels&lt;/h3&gt;
&lt;p&gt;I built a real time, user-configurable, Twitter search app just using Glow:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://hub.tweetfu.com/"&gt;View it here&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://github.com/alexyoung/gobbler-js"&gt;GitHub code and downloads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This demonstrates glow.dom, glow.lang, glow.events, glow.net with cross-domain &lt;span class="caps"&gt;API&lt;/span&gt; calls, and glow.animations.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=WQkpxTDd9YY:223i6g5S3yI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=WQkpxTDd9YY:223i6g5S3yI:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=WQkpxTDd9YY:223i6g5S3yI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/WQkpxTDd9YY" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/10/19/bbc-glow</feedburner:origLink></entry>
 
 <entry>
   <title>Gemcutter</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/z2zC5ofu-_g/gemcutter" />
   <updated>2009-10-09T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/10/09/gemcutter</id>
   <content type="html">&lt;p&gt;I&amp;#8217;ve put all my gems on &lt;a href="http://gemcutter.org/"&gt;gemcutter&lt;/a&gt;.  They were on &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; but seeing as &lt;a href="http://github.com/blog/515-gem-building-is-defunct"&gt;GitHub has dropped building gems&lt;/a&gt; I thought it&amp;#8217;d be useful to move them.&lt;/p&gt;
&lt;h3&gt;Using gemcutter&lt;/h3&gt;
&lt;p&gt;The site makes it look easy, because it is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;gem install gemcutter
gem tumble
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now you&amp;#8217;ll be able to install gems from gemcutter.&lt;/p&gt;
&lt;h3&gt;Publishing your own gems&lt;/h3&gt;
&lt;p&gt;This is easy too.  Write a gemspec file and then build a gem, and push it to gemcutter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;gem build yourgem.gemspec
gem push yourgem.gem
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It might take a few minutes before the gem is available from gemcutter.&lt;/p&gt;
&lt;h3&gt;Automation&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://github.com/technicalpickles/jeweler"&gt;Jeweler&lt;/a&gt; can be used to automate this process &amp;#8212; it can use your git and GitHub settings to automatically generate a new project (including GitHub hosting, test directories/helpers).&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=z2zC5ofu-_g:crrRNaEJNCM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=z2zC5ofu-_g:crrRNaEJNCM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=z2zC5ofu-_g:crrRNaEJNCM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/z2zC5ofu-_g" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/10/09/gemcutter</feedburner:origLink></entry>
 
 <entry>
   <title>Deployment: Diffs and Dependencies</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/7pGNvtDD_50/rsyncdiff-and-depwhack" />
   <updated>2009-09-16T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/09/16/rsyncdiff-and-depwhack</id>
   <content type="html">&lt;p&gt;We moved house recently, so my daily development machine was packed up for a few weeks while I worked off a laptop and a 3G card.  It was like being on holiday except I still had to work.  Anyway, I found a few deployment-related scripts I was working on for my &lt;em&gt;Agile Deployment&lt;/em&gt; series of articles (visit the &lt;a href="/tags.html#agiledeployment"&gt;agiledeployment&lt;/a&gt; tag for more).&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve packaged these scripts as rubygems, so they&amp;#8217;ll be easy to install and try out.  They&amp;#8217;re essentially snippets that I use to maintain sanity when deploying.  They might form part of a bigger deployment system in the future.&lt;/p&gt;
&lt;h3&gt;Rsyncdiff&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/rsyncdiff.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/alexyoung/rsyncdiff"&gt;rsyncdiff&lt;/a&gt; shows the differences between your local and remote code.  I&amp;#8217;ve been using this to try to visualise what will happen after deploying.&lt;/p&gt;
&lt;p&gt;The output contains file creations, deletions and updates.  The updates show a &lt;code&gt;diff&lt;/code&gt;, and will automatically use &lt;code&gt;colordiff&lt;/code&gt; if you&amp;#8217;ve got it.&lt;/p&gt;
&lt;p&gt;I use it with my Rails projects like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;rsyncdiff server:/u/apps/project/current/app app/
rsyncdiff server:/u/apps/project/current/lib lib/
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;I usually just compare &lt;code&gt;app/&lt;/code&gt;, &lt;code&gt;lib/&lt;/code&gt; and &lt;code&gt;public/&lt;/code&gt;, just to keep the output manageable.&lt;/p&gt;
&lt;h3&gt;Depwhack&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/depwhack.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/alexyoung/depwhack"&gt;depwhack&lt;/a&gt; compares the gems your application loads locally with the gems available on a remote server.  I use &lt;code&gt;config.gem&lt;/code&gt; in my Rails project which helps mitigate missing gems, but if I&amp;#8217;m doing a major deploy for a client I don&amp;#8217;t like nasty surprises.&lt;/p&gt;
&lt;p&gt;Ruby has a method called &lt;code&gt;at_exit&lt;/code&gt; which you can use as a callback at the end of a script.  I was using this after running tests on my Rails projects to get a complete list of the gems they use.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s pretty easy to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;at_exit&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="no"&gt;Gem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loaded_specs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&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;version&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;ve put these scripts as gems on &lt;a href="http://gemcutter.org/"&gt;gemcutter&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;gem install rsyncdiff
gem install depwhack
&lt;/pre&gt;
&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=7pGNvtDD_50:wIbIHG-s8fE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=7pGNvtDD_50:wIbIHG-s8fE:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=7pGNvtDD_50:wIbIHG-s8fE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/7pGNvtDD_50" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/09/16/rsyncdiff-and-depwhack</feedburner:origLink></entry>
 
 <entry>
   <title>The End of Religious Wars</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/GoGE2pGdeEQ/end-of-religious-wars" />
   <updated>2009-08-21T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/08/21/end-of-religious-wars</id>
   <content type="html">&lt;p&gt;Are you tired of rockstar drama queens declaring war on languages, burning all their bridges and filling up &lt;a href="http://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt; with boring articles?&lt;/p&gt;
&lt;p&gt;Over the last few years Python and Ruby programmers have had a quiet, seething war.  It&amp;#8217;s equally as ridiculous as Nintendo vs. Sega was back in school.  Conventional wisdom in the gaming community is that these wars start because kids can only afford one of the consoles, so they inevitably take sides.&lt;/p&gt;
&lt;p&gt;This largely accounts for the programming language and editor wars.  After all, time is the greatest currency &amp;#8212; it can take months or years to master a programming language.  The basics are easy to pick up, but fluency takes real effort, which makes people want to defend their investment.&lt;/p&gt;
&lt;h3&gt;The Optimist&lt;/h3&gt;
&lt;p&gt;Reg Braithwaite&amp;#8217;s talk entitled &lt;a href="http://github.com/raganwald/homoiconic/blob/master/2009-05-01/optimism.md"&gt;Learned Optimism&lt;/a&gt; is about a self-improvement technique created by Dr. Martin Seligman.  In the talk, he says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Optimists explain good things as being personal, general, and permanent, and explain away bad things as being impersonal, specific, and temporary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We need to start thinking about our skills as &lt;strong&gt;permanent&lt;/strong&gt; and &lt;strong&gt;general&lt;/strong&gt;.  Learning C++ doesn&amp;#8217;t mean you need to take sides, you&amp;#8217;re free to learn Ruby, Lisp or anything else.  This won&amp;#8217;t make you any less of a C++ programmer.&lt;/p&gt;
&lt;h3&gt;The Superhero&amp;#8217;s Cape&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/cape.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve been dabbling with Ruby since around 2000, and working professionally with it since 2006.  Ruby is my superhero cape.  When I sit down with an editor or &lt;code&gt;irb&lt;/code&gt;, I know exactly what&amp;#8217;s going on.&lt;/p&gt;
&lt;p&gt;The problem with capes is they get caught on things.  We either get stuck or simply dragged down into bone-crunching machinery.&lt;/p&gt;
&lt;p&gt;And I&amp;#8217;d be stuck too if I didn&amp;#8217;t have a bizarre fascination with Lisp and Objective-C.  Mac and iPhone development dragged me kicking and screaming away from Ruby, and you can put Lisp down to latent British genetic eccentricity.&lt;/p&gt;
&lt;h3&gt;The Novice&lt;/h3&gt;
&lt;p style="float:right;"&gt;&lt;img src="/images/amigac.png" style="padding-left: 1em;" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;When I was a kid, I loved videogames.  According to my dad, it&amp;#8217;s what got me into programming in the first place.  So when I started out for real, these questions plagued my mind:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;What programming language do games use?&lt;/li&gt;
	&lt;li&gt;How quickly can I learn enough to make games?&lt;/li&gt;
	&lt;li&gt;What tools do &amp;#8220;real&amp;#8221; developers use?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I went out and got an Amiga C book and started learning C.  This was difficult at a young age, and I kept finding myself messing around in a &lt;span class="caps"&gt;BASIC&lt;/span&gt; environment called Amos (and later Blitz Basic).  I could make cool stuff in Amos and Blitz, but I could only make rudimentary Amiga OS applications in C.&lt;/p&gt;
&lt;p&gt;Reminiscing about this made me realise that when people are starting out, nobody gives them straightforward answers:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The language books claim to teach their language without previous experience, so they&amp;#8217;re marketed to beginners&lt;/li&gt;
	&lt;li&gt;It&amp;#8217;s hard to find good general programming books suited to beginners&lt;/li&gt;
	&lt;li&gt;It&amp;#8217;s hard to find mentors&lt;/li&gt;
	&lt;li&gt;Online communities can be caustic&lt;/li&gt;
	&lt;li&gt;Nobody makes it clear that languages are just tools &amp;#8212; just because you want to make games doesn&amp;#8217;t mean you should learn C++ straight away&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Novices are often impatient, but nobody caters for this.  Nobody says &amp;#8220;you&amp;#8217;ll learn a lot of languages in your time, so let&amp;#8217;s start with something fun&amp;#8221;.  There are projects attempting to reach young people using custom environments, but I think people are put off by wanting to learn &amp;#8220;the real thing&amp;#8221;.&lt;/p&gt;
&lt;p&gt;This early struggle to achieve results is perhaps the most interesting part of a programmer&amp;#8217;s career.  The novice will happily experiment with &lt;strong&gt;everything&lt;/strong&gt; until they gain traction.  If this attitude could be fostered at a later stage in a programmer&amp;#8217;s career, we&amp;#8217;d waste a lot less time being defensive and start to enjoy our jobs again.&lt;/p&gt;
&lt;h3&gt;The Scientist vs. The Artist&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/sand_traveler.png" style="margin: 0 auto;" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="http://www.complexification.net/gallery/machines/sandTraveler/index.php"&gt;The Sand Traveler&lt;/a&gt;: 1,000 traveling particles, each in pursuit of another.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Programmers &amp;#8220;build on the shoulders of giants&amp;#8221; &amp;#8212; it&amp;#8217;s essential to what we do.  Relativity got us to the moon, C got us to widely available Unix.  Programming has a deep relationship with science; it wouldn&amp;#8217;t exist without research in information theory and physics.&lt;/p&gt;
&lt;p&gt;Certain areas of our work need to be evaluated with valid experiments &amp;#8212; benchmarks and optimisation, compiler and interpreter design.  However, this doesn&amp;#8217;t mean programming is a science.  It&amp;#8217;s equally an art.  Aesthetics are important despite being subjective.&lt;/p&gt;
&lt;p&gt;Be wary of people ranting about the right and wrong ways of solving problems.  Try to balance established best practices with your own opinions, ideas and &lt;strong&gt;take time to experiment&lt;/strong&gt;.  An artist doesn&amp;#8217;t use the same techniques throughout their career, they balance the practical and technical aspects of their work with healthy experimentation.  They actively seek out influences and inspiration.&lt;/p&gt;
&lt;h3&gt;Heroes and Villains&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/zxspectrum_48k.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Do a Twitter search for &lt;a href="http://search.twitter.com/search?q=dhh"&gt;dhh&lt;/a&gt; and see how many people openly mock &lt;a href="http://www.loudthinking.com/"&gt;David Heinemeier Hansson&lt;/a&gt;, and how many people say &amp;#8220;wow thanks for Rails &lt;span class="caps"&gt;DHH&lt;/span&gt;!&amp;#8221;&lt;/p&gt;
&lt;p&gt;How many people talked about how important &lt;a href="http://en.wikipedia.org/wiki/Why_the_lucky_stiff"&gt;_why&lt;/a&gt; was to the Ruby community &lt;strong&gt;before&lt;/strong&gt; he disappeared?&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve worked with enough programmers to know that we&amp;#8217;re generally insecure.  We&amp;#8217;re quick to complain, and rarely give out praise.  But it&amp;#8217;s difficult to know what good programming is when people don&amp;#8217;t talk about who the good programmers and technologists are.  We need to properly pay respect to our Jimi Hendrix and Kurt Cobains.&lt;/p&gt;
&lt;p&gt;For example, I love my job but probably wouldn&amp;#8217;t have it if it wasn&amp;#8217;t for Clive Sinclair.  It&amp;#8217;s OK to have heroes!&lt;/p&gt;
&lt;h3&gt;The Effect of Inertia&lt;/h3&gt;
&lt;p style="float:left;"&gt;&lt;img src="/images/algol.png" style="padding-right: 1em;" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Find some &lt;span class="caps"&gt;ALGOL&lt;/span&gt; online.  I actually have an ancient &lt;span class="caps"&gt;ALGOL&lt;/span&gt; manual which talks about punch cards and everything.  The reason I collect these old programming books isn&amp;#8217;t nostalgia, it&amp;#8217;s because we&amp;#8217;re still drawing on work from the 50s and 60s.  Looking at &lt;span class="caps"&gt;ALGOL&lt;/span&gt; reveals how much things have changed, but at the same time makes us realise the fundamentals haven&amp;#8217;t moved on (for imperative languages).&lt;/p&gt;
&lt;p&gt;Ruby took a lot of ideas from languages that came before it, and the clarity of its syntax still impresses people today.  This perceived evolution creates &amp;#8220;inertia&amp;#8221; &amp;#8212; people are swept along in the wake of change.&lt;/p&gt;
&lt;p&gt;Don&amp;#8217;t let this sweep you away from other things though.  You don&amp;#8217;t need to throw away work and switch alliances, just add new languages to your utility belt and enjoy playing with them.&lt;/p&gt;
&lt;h3&gt;The End of Religious Wars&lt;/h3&gt;
&lt;p&gt;We could end religious programming wars by learning to &lt;strong&gt;teach&lt;/strong&gt; programming properly.  Learning programming isn&amp;#8217;t just about covering imperative, functional and object-oriented patterns.  It&amp;#8217;s about culture, tools, engineering, art and science.&lt;/p&gt;
&lt;p&gt;Have you ever seen a programming book that used examples from different languages?  It would be completely possible to create such a book in a new and exciting way.  Imagine a &lt;em&gt;Why&amp;#8217;s Poignant Guide To Programming&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Anything that allows novices to quickly get up to speed will encourage cross-language experimentation and experience.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;em&gt;Free books&lt;/em&gt; encourage people to learn: all languages should have a quality free book available&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Try ruby&lt;/em&gt; allowed people to try out a Ruby interpreter online.  This was great for novices or newcomers to the language &amp;#8212; all languages need projects like this&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Easy installation&lt;/em&gt; is key to letting people dabble.  How many Windows users downloaded &lt;a href="http://instantrails.rubyforge.org"&gt;Instant Rails&lt;/a&gt; to quickly try out Rails?&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Quick results&lt;/em&gt; are great for impatient beginners, and all languages should have &lt;strong&gt;awesome game examples&lt;/strong&gt; instead of dull examples about databases and business software&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Co-operation between languages&lt;/em&gt; might encourage people to work together more &amp;#8212; jruby has had positive effects on ruby&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Experienced programmers&lt;/em&gt; would do well to share their influences and pay respect to truly great programmers.  They need to blog about their heroes before they vanish&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Experienced programmers&lt;/em&gt; should stop fearing what they haven&amp;#8217;t yet learned.  &lt;span class="caps"&gt;CSS&lt;/span&gt;/regexes/&lt;span class="caps"&gt;SQL&lt;/span&gt; aren&amp;#8217;t evil, spend some time learning them properly!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Naysayers will say &amp;#8220;jack of all trades, master of none&amp;#8221;.  The projects and jobs you decide to work on will inevitably lead you to become more experienced in one language.  I&amp;#8217;m not saying you shouldn&amp;#8217;t master a limited subset of things: we should encourage people to experiment and enjoy their work.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=GoGE2pGdeEQ:zftCXVREJtQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=GoGE2pGdeEQ:zftCXVREJtQ:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=GoGE2pGdeEQ:zftCXVREJtQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/GoGE2pGdeEQ" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/08/21/end-of-religious-wars</feedburner:origLink></entry>
 
 <entry>
   <title>What Makes You Happy?</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/dUCtVEAMtVk/happiness" />
   <updated>2009-08-20T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/08/20/happiness</id>
   <content type="html">&lt;p&gt;&lt;a href="https://www.trackyourhappiness.org/"&gt;Track Your Happiness&lt;/a&gt; is an entertaining experiment.  It&amp;#8217;s a site that periodically sends out links to surveys.  The surveys are designed to gather information on your general well-being and happiness.  The surveys work fine on iPhones too, so I was able to fill them out wherever I was.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/happiness.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;It focuses on a few main areas:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Activity&lt;/li&gt;
	&lt;li&gt;Focus&lt;/li&gt;
	&lt;li&gt;Productivity&lt;/li&gt;
	&lt;li&gt;Social interactions&lt;/li&gt;
	&lt;li&gt;Sleep time and sleep quality&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before taking the tests I had these preconceptions:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Sleep doesn&amp;#8217;t bother me&lt;/li&gt;
	&lt;li&gt;Productivity and focus are proportional to happiness&lt;/li&gt;
	&lt;li&gt;I like working alone&lt;/li&gt;
	&lt;li&gt;I prefer reading and playing to working or watching TV&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After taking two or three weeks of tests I finally completed the &amp;#8220;course&amp;#8221; of surveys.  The results were contrary to my assumptions:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Sleep is strongly tied to my happiness&lt;/li&gt;
	&lt;li&gt;Productivity does not relate to my happiness&lt;/li&gt;
	&lt;li&gt;I prefer working with at least one person&lt;/li&gt;
	&lt;li&gt;Reading and watching TV make me more happy than any other activity&lt;/li&gt;
	&lt;li&gt;I&amp;#8217;m happier outside&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these facts surprised me.  When I was a child I&amp;#8217;d play outside all the time, but I still had weekend hacking sessions on the ZX Spectrum.  Also, as an entrepreneur and freelancer, I thought productivity made me happy.  It turns out that my workaholic nature has been compromising my overall well-being.&lt;/p&gt;
&lt;p&gt;Try signing up and sticking with Track Your Happiness throughout the course of surveys.  They can be annoying and repetitive, but you might be surprised by what you discover.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=dUCtVEAMtVk:vlj44Bl95Mk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=dUCtVEAMtVk:vlj44Bl95Mk:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=dUCtVEAMtVk:vlj44Bl95Mk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/dUCtVEAMtVk" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/08/20/happiness</feedburner:origLink></entry>
 
 <entry>
   <title>Agile Deployment: Unified Repository</title>
   <link href="http://feedproxy.google.com/~r/webdevelopernotes/~3/tdzHKd6WO78/agile-deployment-unified-repository" />
   <updated>2009-08-12T00:00:00+01:00</updated>
   <id>http://alexyoung.org/2009/08/12/agile-deployment-unified-repository</id>
   <content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is part 5 of &lt;a href="/tags.html#agiledeployment"&gt;Agile Deployment&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Source code management is closely tied to deployment.  Creating a deployment strategy that truly feels agile requires careful management and preparation of the source repository.&lt;/p&gt;
&lt;h2&gt;Branched Environments&lt;/h2&gt;
&lt;p&gt;My first approach worked like this:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;I had subversion branches for each server type (pre-production, production)&lt;/li&gt;
	&lt;li&gt;These branches had their own settings files suited to their environments&lt;/li&gt;
	&lt;li&gt;We&amp;#8217;d merge changes from forks into trunk, then merge into pre-production, then production&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This worked relatively well and was easy to understand.  The main issue I had with this approach was it added overhead to deploying that I wasn&amp;#8217;t prepared to deal with.  Having to carefully merge new changes every time was a real chore.  I often didn&amp;#8217;t feel secure that we&amp;#8217;d merged in all the changes, and I&amp;#8217;d have to open up &lt;a href="http://connectedflow.com/changes/"&gt;Changes&lt;/a&gt; to visualise the differences.&lt;/p&gt;
&lt;h2&gt;Unified Environments&lt;/h2&gt;
&lt;p&gt;I generally work in very small teams.  Most of Helicoid&amp;#8217;s design and development is still just me, so I don&amp;#8217;t want any overheads at all.  I just want to type &lt;code&gt;rake deploy&lt;/code&gt; and feel secure that my servers are up to date.&lt;/p&gt;
&lt;p&gt;This forced me to unify my deploy environments into one branch.  This is how it works for my Rails projects:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;database.yml&lt;/code&gt; is either 3 files (dev, pre-prod, prod) or 1 file (where production actually refers to the production database settings) &amp;#8212; this depends on the project and QA requirements&lt;/li&gt;
	&lt;li&gt;Most of my projects have their own configuration files.  These are handled like &lt;code&gt;database.yml&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;My &lt;code&gt;deploy.rb&lt;/code&gt; allows me to select which environment I&amp;#8217;m deploying to&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These settings files can then be either symbolically linked or simply copied during deploy.  Environment selection can work either using a command line variable or a prompt in the deploy script.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s how I select environments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;rake deploy &lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then in &lt;code&gt;deploy.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;server&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;production&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;production_app&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;server&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;
  &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;test_app&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Please select an environment with &amp;#39;server=test|production&amp;#39;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;h3&gt;Sanity&lt;/h3&gt;
&lt;p&gt;This unified approach makes every fork deployable to any environment.  Although this removes a lot of overhead, this creates a situation where deployment is so easy that people on your team might deploy to production before your QA processes have reviewed changes.&lt;/p&gt;
&lt;p&gt;One way to mitigate this is by only allowing certain people to have deploy keys.  As with &lt;span class="caps"&gt;SCM&lt;/span&gt;, the software tools can&amp;#8217;t replace communication.  Don&amp;#8217;t rely on the state of the repository alone, talk to your colleagues (and clients if appropriate) to prepare for deployment.&lt;/p&gt;
&lt;p&gt;For small projects I find a group email of feature changes is useful to start this process.  I gather all my changes from my time sheets (I usually have lots of useful stuff in &lt;a href="http://tiktrac.com/"&gt;Tiktrac&lt;/a&gt;), and git logs, then I send out a quick message with bullet points detailing the changes.  This can be a polite way of reminding clients that they need to review and accept changes on the pre-production server.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=tdzHKd6WO78:cdsK07qLUeY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=tdzHKd6WO78:cdsK07qLUeY:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/webdevelopernotes?a=tdzHKd6WO78:cdsK07qLUeY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/webdevelopernotes?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/webdevelopernotes/~4/tdzHKd6WO78" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://alexyoung.org/2009/08/12/agile-deployment-unified-repository</feedburner:origLink></entry>
 
</feed>
