tag:blogger.com,1999:blog-17995579188269252982024-03-19T02:27:54.992-07:00nullJuha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-1799557918826925298.post-88638661140072336542014-12-04T05:16:00.001-08:002014-12-05T05:30:30.058-08:00Screenshots with Mocha-PhantomJS<div style="box-sizing: border-box; color: #333333; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif; font-size: 16px; line-height: 25.6000003814697px; margin-bottom: 16px;">
<div style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
Today I wondered whether you can take screenshots in your <a href="https://github.com/metaskills/mocha-phantomjs" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; box-sizing: border-box; color: #4183c4; text-decoration: none;">mocha-phantomjs</a> tests. Within 15 minutes I found out that my colleagues at Reaktor had already solved the problem in their own fork. So I took a look and with some help got screenshots working in the <a href="https://github.com/Opetushallitus/omatsivut" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; box-sizing: border-box; color: #4183c4; text-decoration: none;">open-source application</a> I'm working on for the Board of Education in Finland. Then I made a <a href="https://github.com/metaskills/mocha-phantomjs/pull/165" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; box-sizing: border-box; color: #4183c4; text-decoration: none;">pull request</a> and now I'm telling you how to make screenshots work.</div>
<div style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
So, in your test code, write a helper for generating a screenshot on demand:</div>
<div class="highlight highlight-javascript" style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
<pre style="background-color: #f7f7f7; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"><span class="pl-st" style="box-sizing: border-box; color: #a71d5d;">function</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">takeScreenshot</span>() {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">if</span> (<span class="pl-s3" style="box-sizing: border-box; color: #0086b3;">window</span>.callPhantom) {
<span class="pl-s" style="box-sizing: border-box; color: #a71d5d;">var</span> date <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">new</span> <span class="pl-en" style="box-sizing: border-box; color: #795da3;">Date</span>()
<span class="pl-s" style="box-sizing: border-box; color: #a71d5d;">var</span> filename <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">=</span> <span class="pl-s1" style="box-sizing: border-box; color: #df5000;"><span class="pl-pds" style="box-sizing: border-box;">"</span>target/screenshots/<span class="pl-pds" style="box-sizing: border-box;">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">+</span> date.<span class="pl-s3" style="box-sizing: border-box; color: #0086b3;">getTime</span>()
<span class="pl-en" style="box-sizing: border-box; color: #795da3;">console</span><span class="pl-s3" style="box-sizing: border-box; color: #0086b3;">.log</span>(<span class="pl-s1" style="box-sizing: border-box; color: #df5000;"><span class="pl-pds" style="box-sizing: border-box;">"</span>Taking screenshot <span class="pl-pds" style="box-sizing: border-box;">"</span></span> <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">+</span> filename)
callPhantom({<span class="pl-s1" style="box-sizing: border-box; color: #df5000;"><span class="pl-pds" style="box-sizing: border-box;">'</span>screenshot<span class="pl-pds" style="box-sizing: border-box;">'</span></span><span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">:</span> filename})
}
}</pre>
</div>
<div style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
In this function you decide where to put the screenshots and how to name them. In my case, I'm putting them under<code style="background-color: rgba(0, 0, 0, 0.0392157); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; font-stretch: normal; line-height: normal; margin: 0px; padding: 0.2em 0px;">target/screenshots</code>.</div>
<div style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
If you want to generate a screenshot for each test failure you just add the following into your test code.</div>
<div class="highlight highlight-javascript" style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
<pre style="background-color: #f7f7f7; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; font-stretch: normal; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; word-wrap: normal;"> afterEach(<span class="pl-st" style="box-sizing: border-box; color: #a71d5d;">function</span> () {
<span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">if</span> (<span class="pl-v" style="box-sizing: border-box; color: #df5000;">this</span>.currentTest.state <span class="pl-k" style="box-sizing: border-box; color: #a71d5d;">==</span> <span class="pl-s1" style="box-sizing: border-box; color: #df5000;"><span class="pl-pds" style="box-sizing: border-box;">'</span>failed<span class="pl-pds" style="box-sizing: border-box;">'</span></span>) {
takeScreenshot()
}
})</pre>
</div>
<div style="box-sizing: border-box; line-height: 25.6000003814697px; margin-bottom: 16px;">
And next time you run your <code style="background-color: rgba(0, 0, 0, 0.0392157); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; font-stretch: normal; line-height: normal; margin: 0px; padding: 0.2em 0px;">mocha-phantomjs</code> tests, KABOOM, you have screenshots. Good times!</div>
<div style="box-sizing: border-box; line-height: 25.6000003814697px;">
Update: A couple of days later, version 3.5.2 with screenshot support was released!</div>
</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com49tag:blogger.com,1999:blog-1799557918826925298.post-6309690435110790882013-12-16T10:18:00.001-08:002013-12-18T14:41:14.021-08:00Mocha on Monads<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
<strong>Disclaimer: This article can be classified as a Monad Tutorial and therefore considered harmful. Proceed at own risk.</strong></div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
The challenge in testing a browser application with <a href="http://visionmedia.github.io/mocha/" style="color: #4183c4; text-decoration: none;">Mocha</a> is that the application behaves asynchronously because of things like page transitions and AJAX. This means that the test code often has to wait for some condition before continuing. And, as we all know, Javascript doesn't have threads and thus we cannot block when we wait. This means we need to use callbacks.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
And because writing asynchronous code is tough, the test writer (me) avoids it and favors writing tests synchronously except when absolutely necessary. And sometimes they write a test synchronously even if the application actually behaves asynchronously. And often they get away with it, because the test passes. Until it doesn't. It may, for instance, pass on Chrome in 99% of the cases. But when you have a hundred test cases, that's not going to be enough.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
And when something that used to be synchronous becomes asynchronous, shit will really hit the fan if the tests were sloppy. In my experience this happens quite often. For example, you add an AJAX call somewhere. Bang!</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Mocha supports asynchronous testing for sure. You can use callback-style functions in your <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">before</code>s and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">it</code>s. When you need to perform a long sequence of (possibly asynchrous) operations, you may use a library such as <a href="https://github.com/mtkopone/zhain" style="color: #4183c4; text-decoration: none;">zhain</a> and you're a bit better off.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
But you still can't beat the convenience of normal synchronous Javascript blocks, when it comes to passing any state from one of your async operations to another. So you'll start with code like this.</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"> <span class="kd" style="font-weight: bold;">var</span> <span class="nx">button</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#loginForm button"</span><span class="p">)</span>
<span class="nx">button</span><span class="p">.</span><span class="nx">click</span><span class="p">()</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">customerId</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#customerId"</span><span class="p">).</span><span class="nx">text</span><span class="p">()</span>
<span class="nx">assertEquals</span><span class="p">(</span><span class="mi" style="color: #009999;">666</span><span class="p">,</span> <span class="nx">customerId</span><span class="p">)</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
And when things get async, you will end up with code looking like this.</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"> <span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#loginForm button"</span><span class="p">)</span> <span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">button</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">button</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#customerId"</span><span class="p">)</span> <span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">customerIdElement</span><span class="p">)</span> <span class="p">{</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">customerId</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">customerIdElement</span><span class="p">.</span><span class="nx">text</span><span class="p">()</span>
<span class="nx">assertEquals</span><span class="p">(</span><span class="mi" style="color: #009999;">666</span><span class="p">,</span> <span class="nx">customerId</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">})</span>
<span class="kd" style="font-weight: bold;">function</span> <span class="nx">findElement</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="kd" style="font-weight: bold;">function</span> <span class="nx">f</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">{</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">element</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span>
<span class="k" style="font-weight: bold;">if</span> <span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">done</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span>
<span class="p">}</span> <span class="k" style="font-weight: bold;">else</span> <span class="p">{</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">f</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">},</span> <span class="mi" style="color: #009999;">100</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Not a very complex case yet, yet a <a href="http://travistidwell.com/presentations/jquery-node-phantom/images/callback_hell.jpg" style="color: #4183c4; text-decoration: none;">Callback Hell</a> is starting to form here.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
What if, just what if, you could write it like this instead:</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"> <span class="nx">button</span> <span class="o" style="font-weight: bold;"><- <span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#loginForm button"</span><span class="p">)</span>
<span class="nx">button</span><span class="p">.</span><span class="nx">click</span><span class="p">()</span>
<span class="nx">customerIdElement</span> <span class="o" style="font-weight: bold;"><- <span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#customerId"</span><span class="p">)</span>
<span class="kd" style="font-weight: bold;">let</span> <span class="nx">customerId</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">customerIdElement</span><span class="p">.</span><span class="nx">text</span><span class="p">()</span>
<span class="nx">assertEquals</span><span class="p">(</span><span class="mi" style="color: #009999;">666</span><span class="p">,</span> <span class="nx">customerId</span><span class="p">)</span>
<!-----></-></span><!-----></-></span></pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
In this form, the asynchronous calls do not break the flow. You just indicate an asynchronous call with the arrow symbol<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;"><- code="">. Nice?<!-----></-></code></div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Shouldn't be too hard to implement a precompiler that would automatically convert this to the former callbacky form.</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: #333333; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_12_15_Mocha_on_Monads.md#a-new-abstraction" name="a-new-abstraction" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>A New Abstraction</h2>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Quizz: What is the abstraction that allows the fancy syntax above to be applied not only to callbacks but to arbitrary constructs sharing a couple of common traits? Which programming language supports this syntax and automatically "desugars" it?</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
In this abstraction, you need a method for chaining things together. Using Haskell syntax, it would look like</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">chain :: M a -> (a -> M b) -> M b
</code></pre>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
But let's call it the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">chain</code> method. And stick to Javascript, with a little sugar. By sugar I mean support for this fancy syntax extension. Not actual sugar.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Anyway, the actual code might look like this:</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><span class="k" style="font-weight: bold;">do</span> <span class="p">{</span>
<span class="nx">button</span> <span class="o" style="font-weight: bold;"><- <span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#loginForm button"</span><span class="p">)</span>
<span class="nx">button</span><span class="p">.</span><span class="nx">click</span><span class="p">()</span>
<span class="nx">customerIdElement</span> <span class="o" style="font-weight: bold;"><- <span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#customerId"</span><span class="p">)</span>
<span class="kd" style="font-weight: bold;">let</span> <span class="nx">customerId</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">customerIdElement</span><span class="p">.</span><span class="nx">text</span><span class="p">()</span>
<span class="nx">assertEquals</span><span class="p">(</span><span class="mi" style="color: #009999;">666</span><span class="p">,</span> <span class="nx">customerId</span><span class="p">)</span>
<span class="p">}</span>
<!-----></-></span><!-----></-></span></pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
And this would be automatically preprocessed into</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#loginForm button"</span><span class="p">).</span><span class="nx">chain</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">button</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">button</span><span class="p">.</span><span class="nx">click</span><span class="p">()</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">findElement</span><span class="p">(</span><span class="s2" style="color: #dd1144;">"#customerId"</span><span class="p">).</span><span class="nx">chain</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">customerIdElement</span><span class="p">)</span> <span class="p">{</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">customerId</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">customerIdElement</span><span class="p">.</span><span class="nx">text</span><span class="p">()</span>
<span class="nx">assertEquals</span><span class="p">(</span><span class="mi" style="color: #009999;">666</span><span class="p">,</span> <span class="nx">customerId</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">})</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Which is vanilla Javascript again.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
To make this work, the asynchronous operations, like the object returned by <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">findElement</code>, need a method named<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">chain</code>.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
We might implement it like here.</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"> <span class="nx">Async</span> <span class="o" style="font-weight: bold;">=</span> <span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">if</span> <span class="p">(</span><span class="o" style="font-weight: bold;">!</span><span class="p">(</span><span class="k" style="font-weight: bold;">this</span> <span class="k" style="font-weight: bold;">instanceof</span> <span class="nx">Async</span><span class="p">))</span> <span class="k" style="font-weight: bold;">return</span> <span class="k" style="font-weight: bold;">new</span> <span class="nx">Async</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span>
<span class="k" style="font-weight: bold;">this</span><span class="p">.</span><span class="nx">run</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">f</span>
<span class="p">}</span>
<span class="nx">Async</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">chain</span> <span class="o" style="font-weight: bold;">=</span> <span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span> <span class="p">{</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">self</span> <span class="o" style="font-weight: bold;">=</span> <span class="k" style="font-weight: bold;">this</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">Chain</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">self</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">next</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">f</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span>
<span class="nx">next</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">})</span>
<span class="p">}</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
And the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">findElement</code> method would change to</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"> <span class="kd" style="font-weight: bold;">function</span> <span class="nx">findElement</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">Async</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span> <span class="nx">f</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">{</span>
<span class="kd" style="font-weight: bold;">var</span> <span class="nx">element</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span>
<span class="k" style="font-weight: bold;">if</span> <span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">done</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span>
<span class="p">}</span> <span class="k" style="font-weight: bold;">else</span> <span class="p">{</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">f</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">},</span> <span class="mi" style="color: #009999;">100</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">})</span>
<span class="p">}</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
The only change here is that we wrap the returned function using Async.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Now, given we had this preprocessor, we should be able to use our fancy new syntax with asynchronous operations as long as they are wrapped using <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Async</code>.</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: #333333; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_12_15_Mocha_on_Monads.md#some-composition" name="some-composition" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>Some composition</h2>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Now what if we wanted to write a function <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">getText</code> that simply calls <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">findElement</code> and returns the element text by calling <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">element.text()</code>?</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
It would look like this.</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><span class="kd" style="font-weight: bold;">function</span> <span class="nx">getText</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">Async</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">findElement</span><span class="p">(</span><span class="nx">selector</span><span class="p">)(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">done</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span>
<span class="p">})</span>
<span class="p">})</span>
<span class="p">}</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Ugly huh? Fancy syntax to the resque:</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><span class="kd" style="font-weight: bold;">function</span> <span class="nx">getText</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="k" style="font-weight: bold;">do</span> <span class="nx">Async</span> <span class="p">{</span>
<span class="nx">element</span> <span class="o" style="font-weight: bold;"><- <span class="nx">findElement</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">element</span><span class="p">.</span><span class="nx">getText</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<!-----></-></span></pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
The <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">do Async</code> part here is to tell the preprocessor to use namely the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Async</code> abstraction. And <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">return</code> is not your typical Javascript return. In this syntax it is used to wrap a simple expression into an Async operation. This function would be converted to the following piece of Javascript.</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><span class="kd" style="font-weight: bold;">function</span> <span class="nx">getText</span><span class="p">(</span><span class="nx">selector</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">findElement</span><span class="p">(</span><span class="nx">selector</span><span class="p">).</span><span class="nx">chain</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="nx">Async</span><span class="p">.</span><span class="nx">of</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">getText</span><span class="p">())</span>
<span class="p">})</span>
<span class="p">}</span>
</pre>
</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
So the preprocessor would convert <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">return x</code> into <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Async.of(x)</code>. This means we have to implement an <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">of</code> method into <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Async</code>. Like here.</div>
<div class="highlight highlight-js" style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; overflow-x: auto; overflow-y: hidden;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px; word-wrap: normal;"><span class="nx">Async</span><span class="p">.</span><span class="nx">of</span> <span class="o" style="font-weight: bold;">=</span> <span class="nx">Async</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">of</span> <span class="o" style="font-weight: bold;">=</span> <span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k" style="font-weight: bold;">return</span> <span class="k" style="font-weight: bold;">new</span> <span class="nx">Async</span><span class="p">(</span><span class="kd" style="font-weight: bold;">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">callback</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">}</span>
</pre>
</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: #333333; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_12_15_Mocha_on_Monads.md#conclusion" name="conclusion" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>Conclusion</h2>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
As you might have deduced already, what I described above is a Javascripty version of Haskell's <a href="http://en.wikibooks.org/wiki/Haskell/do_Notation" style="color: #4183c4; text-decoration: none;">do-notation</a> for Monads. The process of conververting do-notation into calls to the Monad methods is called <em>desugaring</em>.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
But simply put, Monads are just things that support <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">of</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">chain</code>. These functions have different names in different environments (<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">return</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">>>=</code> in Haskell), but here I've chosen to use the names used in the <a href="https://github.com/fantasyland/fantasy-land" style="color: #4183c4; text-decoration: none;">fantasy-land</a>specification for Javascript.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Had we this notation in Javascript, we could use is for surprisingly many things, including <a href="https://github.com/fantasyland/fantasy-promises" style="color: #4183c4; text-decoration: none;">Promises</a> and <a href="https://github.com/fantasyland/fantasy-options" style="color: #4183c4; text-decoration: none;">Options</a>. And the notation is not the only benefit of the Monads; there's a lot more you can build on top of this common interface.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
For the record, the <a href="http://roy.brianmckenna.org/" style="color: #4183c4; text-decoration: none;">Roy</a> language supports a very similar do-notation as described above. The thing with Roy is though that it is quite remote to actual Javascript and is not an easy replacement as-is. But on the Roy website, you can play with the do-notation (Monad) examples and see how it desugars the code, in case you're interested.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
The <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Async</code> Monad here is perhaps more widely known as the <a href="http://hackage.haskell.org/package/mtl-1.1.0.2/docs/Control-Monad-Cont.html" style="color: #4183c4; text-decoration: none;">Continuation Monad</a>. For the intended purpose (asynchronous testing in Javascript) a bit better suited Monad would be one that allows error propagation too, possibly using <a href="http://howtonode.org/control-flow-part-ii" style="color: #4183c4; text-decoration: none;">Node-style callbacks</a>. In any case, all code here goes without warranties as I haven't actually run it.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Oh, and I'm sure some of you know how to write compilers and stuff, so pls make this precompiler and ship it to me on Github.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
<em>... some time passes ...</em></div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Oh, indeed the gods have forseen my desire and implemented a <a href="http://sweetjs.org/" style="color: #4183c4; text-decoration: none;">sweet.js</a> based <a href="https://github.com/puffnfresh/sweet-fantasies/blob/master/test/do.js" style="color: #4183c4; text-decoration: none;">do-notations implementation</a> for Javascript.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-top: 15px;">
KTHXBYE.</div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com12tag:blogger.com,1999:blog-1799557918826925298.post-26270237654601453462013-10-10T10:19:00.003-07:002013-10-11T12:16:54.467-07:00Asynchronous Functional Testing with Mocha and JQuery<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: #333333; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; line-height: 1.7; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; padding: 0px; position: relative;">
Context</h2>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Me and my friends been doing functional testing on our rather large single-page app, using Mocha and JQuery. JQuery is used to drive the system under test (SUT), usually started in an IFrame. JQuery is also used to verify that the application behaves as expected. For instance, in a todo-application, you might</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">S("#addTodo").click()
expect(S("#tasks").length).to.equal(1)
</code></pre>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Here S is a helper function that delegates to the JQuery instance in the SUT window. So the basic pattern is that you perform certain operations on the SUT and then verify that the application ends up in the expected end-state. In mocha, you might do this like</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">describe("Add todo button", function() {
before(function() {
S("#addTodo").click()
})
it("Adds todo", function() {
expect(S("#todos").length).to.equal(1)
})
})
</code></pre>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
If the application is synchronous this works just fine.</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: #333333; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_10_10_Asynchoronous_Mocha.md#problem" name="problem" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>Problem</h2>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
You might have guessed this. The minute you add your first asynchronous thing in the application, you enter the world of uncertain test results. That is, if your tests assume that after doing X, the application goes from state A to B. To get past this, you may use asynchronous functions in your <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">before</code> blocks. Like</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">before(function(done) {
wait.until(function() { return S("#todos").length == 2}, done)
})
</code></pre>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
This will fix a single test case. Everytime you find that some test is unreliable, you add some kind of an ad-hoc asynchoronous wait. Now step to a situation where you have a hundred tests and you change your application so that something that used to be synchronous is now asynchronous. You'll get some failing tests. You may get tests that fail on some browsers and only sometimes. Horror!</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Adding async waits to individual test setup steps doesn't scale. Been there, done that.</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; color: #333333; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_10_10_Asynchoronous_Mocha.md#solution" name="solution" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>Solution</h2>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
I strongly believe that we need a more systematic solution here. The developer shouldn't need to consider asyncronicity at each test step.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Why not go for a solution where after each <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">before</code> block and before each <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">it</code>, there should be an implicit async wait that would ensure that before the test proceeds, the framework ensures that all AJAX calls, animations, transitions, page loads and whatnot have finished.</div>
<div style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 15.555556297302246px; line-height: 27.77777862548828px; margin-bottom: 15px; margin-top: 15px;">
Is this hard? Shouldn't be. Just monkey-patch <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">before</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">it</code> to include these steps. Gonna try this and write more afterwards. What do you think?</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com11tag:blogger.com,1999:blog-1799557918826925298.post-20082584295684085572013-02-03T06:19:00.000-08:002013-02-03T06:19:40.296-08:00New Bacon.js blog<span style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">I added a new blog for </span><a href="http://baconjs.blogspot.fi/" style="background-color: white; border: 0px; color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px; text-decoration: initial;">Bacon.js</a><span style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">. The Bacon.js related post will be there from now on. I figured this blog is a bit hard to find, considering the amount of typos in the name, for one thing. So, see you </span><a href="http://baconjs.blogspot.fi/" style="background-color: white; border: 0px; color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px; text-decoration: initial;">there</a><span style="background-color: white; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">!</span>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com8tag:blogger.com,1999:blog-1799557918826925298.post-73355583327833005802013-02-03T05:52:00.002-08:002013-08-27T03:14:34.959-07:00Chicken, Egg and Bacon.js<br />
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
FRP is easy when you can define your "event network" first and then just watch it make profit. For instance, you may define EventStreams and Properties based on DOM events, compose them, do some AJAX and show the result in the UI, just like we did in the Bacon.js Tutorial parts II & III.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Sooner or later, though, you'll run into the "chicken and egg" problem: you need to base your streams on something that will change on the fly. So, you cannot just define your streams based on existing DOM objects, because the DOM will change.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
For example, if you want to implement the quite well-known <a href="http://addyosmani.github.com/todomvc/" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">TodoMVC</a> application with Bacon.js, you'll have to solve this problem. I suggest you take a look at TodoMVC yourself, but here's a brief description of the problem. Let's start with a screenshot.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
<a href="https://raw.github.com/addyosmani/todomvc/gh-pages/screenshot.png" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;" target="_blank"><img alt="TodoMVC" src="https://raw.github.com/addyosmani/todomvc/gh-pages/screenshot.png" style="border: 0px; box-sizing: border-box; margin: 0px; max-width: 100%; padding: 0px;" /></a></div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
The TodoMVC application is a simple task manager; you add new tasks, mark them complete, edit them and delete them. You have the All, Active and Completed views. Stuff like that.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Assuming you've read the previous log posts and done some Bacon.js coding, you'd hack most of the app together in a couple of hours, save for the tough part. The tough part is the dynamic view, say <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">TodoListView</code>, which lists the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Todos</code> that match the current filter (All/Active/Completed). In fact, the only tough part of the view is that the view also allows you to edit and delete the tasks.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now let's start with the assumption that we want to define a Property, say <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code> that captures the whole data model of the application. The value of this Property will be an Array of Todos, i.e. something like this:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">[
{ id: 1, title: "Buy Bacon", completed: false },
{ id: 2, title: "Buy Eggs", completed: false }
]
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This model would make it very easy to re-render the list whenever the property changes. Also it would be easy to show only completed or active task by applying a filter:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">activeTodos</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todosProperty</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">where</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">({</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">completed</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="kc" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">false</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">})})</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
The number of active tasks could be derived like this:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">var activeTaskCount = activeTodos.map(".length")
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
To simplify a bit, the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code> itself would now depend on item additions only, so we could define the property in a very FRP'ish way:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTodos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">;</span> <span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// stream of new Todo items</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todosProperty</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">addedItems</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">scan</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([],</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">concat</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">])</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
In real life, there are more factors involved, but let's stick to this assumption for now.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#if-the-list-was-not-editable" name="if-the-list-was-not-editable" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>If the list was not editable</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
If the list items were not editable, it would be easy to render the list based on the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code>:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todosProperty</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">;</span> <span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// a Property containing an Array of Todos</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">;</span> <span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// jQuery element</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todosProperty</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">onValue</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">children</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">().</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">remove</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">each</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoElement</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">renderTodoView</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">append</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
We just redraw the list each time <em style="border: 0px; margin: 0px; padding: 0px;">anything</em> changes. The list items are not editable so we don't have to capture any data from them. Just render and forget.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#if-the-view-was-static" name="if-the-view-was-static" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>If the view was static</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
On the other hand, if view was static, i.e. the set of displayed items was not changing, things would be quite easy too. Now you could capture the edits for the Todo items with something like this:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">;</span> <span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// list of todos</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">;</span> <span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// jQuery element</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">properties</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// call renderer function, returns a jQuery element</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoElement</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">renderTodoView</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">append</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// Use Bacon.UI helpers to create Properties for the "title" and "completed" fields</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">combineTemplate</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">({</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">title</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">UI</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">textFieldValue</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">find</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".edit"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">),</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">title</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">completed</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">UI</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">checkBoxValue</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">find</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".toggle"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">completed</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// now we convert this list of Properties to a single property</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// each value of which is an Array of Properties</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todosProperty</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">combineTemplate</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">properties</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Nice and easy. Render Todos once, collect editor values into a single Property using a couple of <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">combineTemplate</code> calls and we've re-defined the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code>.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#but-its-dynamic-and-editable" name="but-its-dynamic-and-editable" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>But it's dynamic and editable</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
So it happens to be the case that the value of <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code> depends on user's interaction with the DOM elements corresponding each Todo item on the list. And these DOM elements will be added and removed based on changes to the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code>. Classic chicken-egg.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
The point of this blog post is to present some of the known solutions to this problem.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#event-delegation-aka-live-bindings" name="event-delegation-aka-live-bindings" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Event delegation (a.k.a live bindings)</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
One way around that chicken-egg problem with dynamically-created views is to use event delegation; bind event handlers to a containing element, and use a specific selector as the second argument to asEventStream. In TodoMVC, we could try something like</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">itemEdits</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">asEventStream</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">"keyup"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".edit"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This would give us all keyup events from child elements of <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">listElement</code>, matching the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">.edit</code> selector. Practically we'd get events when the user edits any of the Todo titles on the list.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now we could re-define <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code> to take these events into account. But before that we'd have to take care of something: we need to include some identifier into the events in order to know which Todo was actually edited. One way to do this would be to include a <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">data-todo-id</code> attribute to the text input element. This wouldn't be a big deal especially if we used a templating lib like Handlebars.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Assuming the text input element had this data attribute, we could continue like</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">itemEdits</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listElement</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">asEventStream</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">"keyup"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".edit"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">event</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">textField</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">$</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">event</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">target</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">textField</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">attr</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">"data-todo-id"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">),</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTitle</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">textField</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">val</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Which would give us a stream of all item edits. For example</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">{ id: 1, newTitle: "Buy a lot of bacon" }
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Re-implementing the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code> could now be done in a number of different ways. For some reason, I like to model this by constructing a number of streams containing <em style="border: 0px; margin: 0px; padding: 0px;">modifications</em> to the list, where the modifications are actually <em style="border: 0px; margin: 0px; padding: 0px;">functions</em> that take the previous list of Todos and return the modified list. So we might say</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">itemModifications</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">itemEdits</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">edit</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">if</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">==</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">edit</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">extend</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">clone</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">),</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">title</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">edit</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTitle</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">else</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This converts the stream of edits into a stream of functions that will transform the list of todos by changing the title of a single todo. We'd also have to convert the stream of new Todos into this form, but I'll leave that as an excercise for you. The <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code>would now look like</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todosProperty</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">itemModifications</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">merge</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">itemAdditions</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">scan</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([],</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So, for some cases, we can come by the chicken-egg problem by using jQuery smartly. But at least I find this a bit inelegant and not very scalable; speaking in MVC terms, you have to create your Views before you create your Model because the latter depends on the former.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#apply-some-mvc" name="apply-some-mvc" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Apply some MVC</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
At this point I'm struggling not to bake in some smart-ass analogy involving chickens, eggs and bacon. And speaking of MVC, there's another way to get by the chicken-egg problem.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So far we've kept trying to define <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoProperty</code> in the traditional FRP way, which is, by composing from a fixed set of source streams. This is elegant to some point. But sometimes it may be more practical and arguably more elegant to make a switch from pull to push. And use <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Bacon.Bus</code>.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Instead of defining all of the event sources at once, we can introduce a Bus, which allows us to explicitly <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">push()</code> events to the stream. It also allows us to <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">plug()</code> in streams after the creation of <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todosProperty</code>.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So let's take a step towards a more traditional MVC architecture and use a stateful model object to represent the list of Todos. The UI can tell this model to change using methods like <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">addTodo</code>, <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">removeTodo</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">modifyTodo</code>.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
We'll use a variable, <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todos</code> in the list model to hold the current list of Todos.</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">TodoListModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">[]</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">changes</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">new</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bus</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoProperty</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">changes</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">toProperty</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">update</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">changes</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">push</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">addTodo</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">update</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">concat</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">])</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifyTodo</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">updatedTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">update</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">===</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">updatedTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">?</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">updatedTodo</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This model object now exposes the field <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoProperty</code> which can be used like in the earlier examples. You might notice that I'm using the exact same functions for the actual list modifications too. The idea is to expose the required set of imperative-style mutator functions that will modify the model's state and push the new list of Todos to the Bus object <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">changes</code>. The exposed <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoProperty</code>now reflects the current state of the model, which is the latest list pushed to <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">changes</code>.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now it's easy to derive Properties like <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">activeTodos</code>, <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">completedTodos</code>, <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">activeTodoCount</code> etc in the FRP way.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
To summarize, we use imperative style for changing state, but expose the state as FRP EventStreams and Properties. One might say that we gain the best of both worlds.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This approach may get easier by using, for instance, an actual Backbone model and expose its state as EventStreams and Properties, as in the <a href="https://github.com/pyykkis/todomvc/blob/bacon-backbone-require/labs/dependency-examples/bacon_backbone_require/src/models/todo_list.coffee" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">Backbone-Bacon TodoMVC list model</a> by Jarno Keskikangas. He's even put a <a href="https://github.com/pyykkis/Backbone.EventStreams" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">Backbone.EventStreams</a> library on Github. In his own words,</div>
<blockquote style="border-left-color: rgb(221, 221, 221); border-left-style: solid; border-width: 0px 0px 0px 4px; color: #777777; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 15px 0px; padding: 0px 15px; quotes: none;">
<div style="border: 0px; padding: 0px;">
Bacon gives superpowers to Backbone controllers.</div>
</blockquote>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Amen.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#one-step-back-towards-frp" name="one-step-back-towards-frp" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>One step back towards FRP</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
If you feel uncomfortable with variables (I do), you might want to revise the MVC solution above. Instead of using a variable, we'll use<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">scan</code>:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">TodoListModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// A Bus of modification functions</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">new</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bus</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoProperty</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">scan</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([],</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">update</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">push</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// no changes to the rest...</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So that's the same <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">scan</code> approach as in the pure FRP solution.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#yet-another-step-towards-frp" name="yet-another-step-towards-frp" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Yet another step towards FRP</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
This is the way I actually implemented TodoMVC. And submitted a Pull Request too.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Instead of exposing imperative mutators in the model, you can expose corresponding Bus objects. This allows you to plug-in source streams to the model. The model would look like this:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">TodoListModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">mapTodos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">f</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">f</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifyTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">updatedTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">mapTodos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">===</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">updatedTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">?</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">updatedTodo</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">addTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">concat</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">newTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">])</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoAdded</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">new</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bus</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoModified</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">new</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bacon</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">Bus</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoAdded</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">addTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">merge</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoModified</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifyTodo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">))</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">this</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoProperty</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">scan</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">([],</span> <span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modification</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
It the View code you'll plug-in streams to the exposed Buses <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoAdded</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoModified</code>. For instance, this is how you might implement a simplified view responsible of rendering a single Todo row.</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">TodoView</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<b>var </b><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">element</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">render</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// use Handlebars or whatnot</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">titleChanges</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">element</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">find</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".edit"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">).</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">asEventStream</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">"keyup"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".target"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">).</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">$</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">).</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="s2" style="border: 0px; color: #dd1144; margin: 0px; padding: 0px;">".val"</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">titleChanges</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">map</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">title</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">title</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">title</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">completed</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">completed</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoModified</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">plug</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
One advantage of this approach, compared to the previous one with the imperative mutators is that when you expose the buses<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoAdded</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">todoModified</code> you're also exposing the same things as streams. So now your View components can subscribe to these streams to react to Todo additions and modifications!</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
There's a little catch here too: you should make sure you apply an "end condition" to the input streams you plug into the model Buses. If you don't do this, the Bus will have references to the streams even after the corresponding UI components have been removed. In practise, you'll probably remove the UI components based on an event in some EventStream, so you can use this same stream for ending the input streams. For example, in <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">TodoView</code> you might have a <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">deleted</code> stream that signals the deletion of this particular Todo. Just add <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">.takeUntil(deleted)</code> to your modifications stream and it will end on deletion:</div>
<div class="highlight" style="background-color: white; border: none; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">TodoView</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">,</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="c1" style="border: 0px; color: #999988; font-style: italic; margin: 0px; padding: 0px;">// .. begins as before ..</span>
<span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">var</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">deleted</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">=</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoProperty</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">changes</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">filter</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todos</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">)</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span>
<span class="k" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">return</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">_</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">where</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">({</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span><span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">:</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todo</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">id</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">}).</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">length</span> <span class="o" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">==</span> <span class="mi" style="border: 0px; color: #009999; margin: 0px; padding: 0px;">0</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">deleted</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">onValue</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="kd" style="border: 0px; font-weight: bold; margin: 0px; padding: 0px;">function</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">{</span> <span class="nx" style="border: 0px; margin: 0px; padding: 0px;">element</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">remove</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">()</span> <span class="p" style="border: 0px; margin: 0px; padding: 0px;">})</span>
<span class="nx" style="border: 0px; margin: 0px; padding: 0px;">listModel</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">todoModified</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">plug</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">modifications</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">.</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">takeUntil</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">(</span><span class="nx" style="border: 0px; margin: 0px; padding: 0px;">deleted</span><span class="p" style="border: 0px; margin: 0px; padding: 0px;">))</span>
<span class="p" style="border: 0px; margin: 0px; padding: 0px;">}</span>
</pre>
</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
You may have a look at the full <a href="https://github.com/raimohanska/todomvc/blob/bacon-jquery/labs/architecture-examples/baconjs/js/app.js" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">Bacon.js TodoMVC implementation</a> for reference. I'm sorry for the tab indentation. That's the standard for TodoMVC. Also, don't expect the implementation to be <em style="border: 0px; margin: 0px; padding: 0px;">exactly</em> the same as in any of the above examples. But it definitely is based on a TodoListModel that exposes Buses and streams to the Views.</div>
<h1 style="-webkit-font-smoothing: antialiased; border: 0px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 28px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2013_02_02_Chicken_Egg_Bacon.md#conclusion" name="conclusion" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Conclusion</h1>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
We've actually covered two related problems in the pure FRP approach, both of which stem from having to introduce some Views before the Model because the Model depends on the Views.</div>
<ol style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li style="border: 0px; margin: 0px; padding: 0px;">The Chicken-Egg Problem - your Model depends on your Views which depend on the Model</li>
<li style="border: 0px; margin: 0px; padding: 0px;">The Coupling Problem - you have to Introduce some View components before the Model, so you cannot properly modularize your application</li>
</ol>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-top: 15px; padding: 0px;">
The first problem in isolation can be in some cases solved by using techniques like jQuery event delegation. But to address the problem in the bigger picture, you need to apply architectural solutions, such as introducing a Model object with either mutator functions or pluggable Bus objects.</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com18tag:blogger.com,1999:blog-1799557918826925298.post-33704439986975030672012-12-11T13:01:00.002-08:002013-10-17T01:56:14.516-07:00Bacon.js Tutorial Part III : AJAX and Stuff<br />
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
This is the next step in the Bacon.js tutorial series. I hope you've read <a href="http://nullzzz.blogspot.fi/2012/11/baconjs-tutorial-part-ii-get-started.html" style="color: #4183c4; text-decoration: none;">Part II</a> already! This time we're going to implement an "as you type" username availability check with AJAX. The steps are</div>
<ol style="font-size: 15px; line-height: 25px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li>Create an <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">EventStream</code> of AJAX requests for use with <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">jQuery.ajax()</code></li>
<li>Create a stream of responses by issuing an AJAX request for each request event and capturing AJAX responses into the new stream.</li>
<li>Define the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">usernameAvailable</code> Property based on the results</li>
<li>Some side-effects: disable the Register button if username is unavailable. Also, show a message.</li>
</ol>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
I suggest you checkout the <a href="https://github.com/raimohanska/bacon-devday-code" style="color: #4183c4; text-decoration: none;">example code</a> and switch to the <a href="https://github.com/raimohanska/bacon-devday-code/tree/tutorial-2" style="color: #4183c4; text-decoration: none;">tutorial-2 branch</a> which will be the starting point for the coding part of this posting. If you just want to have a look at what we'ge got so far, have a <a href="https://github.com/raimohanska/bacon-devday-code/blob/tutorial-2/index.html" style="color: #4183c4; text-decoration: none;">peek</a>.</div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
So, at this point we've got a Property called <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">username</code> which represents the current value entered to the username text input field. We want to query for username availability each time this property changes. First, to get the stream of changes to the property we'll do this:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">username.changes()
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
This will return an <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">EventStream</code>. The difference to the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">username</code> Property itself is that there's no initial value (the empty string). Next, we'll transform this to a stream that provides jQuery compatible AJAX requests:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">availabilityRequest = username.changes().map(function(user) { return { url: "/usernameavailable/" + user }})
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
The next step is extremely easy, using Bacon.UI.js:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">availabilityResponse = availabilityRequest.ajax()
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
This maps the requests into AJAX responses. Looks very simple, but behind the scene it takes care of request/response ordering so that you'll only ever get the response of the latest issued request in that stream. This is where, with pure jQuery, we had to resort to keeping a counter variable for making sure we don't get the wrong result because of network delays.</div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
So, what does the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">ajax()</code> method in Bacon.UI look like? Does it do stuff with variables? Lets see.</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">Bacon.EventStream.prototype.ajax = function() {
return this["switch"](function(params) { return Bacon.fromPromise($.ajax(params)) })
}
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Not so complicated after all. But let's talk about <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMap</code> now, for a while, so that you can build this kind of helpers yourself, too.</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; cursor: text; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2012_11_18_Bacon_Tutorial_III.md#ajax-as-a-promise" name="ajax-as-a-promise" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>AJAX as a Promise</h2>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
AJAX is asynchronous, hence the name. This is why we can't use <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">map</code> to convert requests into responses. Instead, each AJAX request/response should be modeled as a separate stream. And it can be done too. So, if you do</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">$.ajax({ url : "/usernameavailable/jack"})
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
you'll get a jQuery <a href="http://api.jquery.com/category/deferred-object/" style="color: #4183c4; text-decoration: none;">Deferred</a> object. This object can be thought of as a <a href="http://wiki.commonjs.org/wiki/Promises/A" style="color: #4183c4; text-decoration: none;">Promise</a> of a result. Using the Promise API, you can handle the asynchronous results by assigning a callback with the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">done(callback)</code> function, as in</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">$.ajax({ url : "/usernameavailable/jack"}).done(function(result) { console.log(result)})
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
If you try this in you browser, you should see <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">true</code> printed to the console shortly. You can wrap any Promise into an EventStream using <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Bacon.fromPromise(promise)</code>. Hence, the following will have the same result:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">Bacon.fromPromise($.ajax({ url : "/usernameavailable/jack"})).log()
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
This is how you make an EventStream of an AJAX request.</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; cursor: text; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2012_11_18_Bacon_Tutorial_III.md#ajax-with-flatmap" name="ajax-with-flatmap" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>AJAX with <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: inherit; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMap</code></h2>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
So now we have a stream of AJAX requests and the knowhow to create a new stream from a jQuery AJAX. Now we need to</div>
<ol style="font-size: 15px; line-height: 25px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li>Create a response stream for each request in the request stream</li>
<li>Collect the results of all the created streams into a single response stream</li>
</ol>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
This is where <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMap</code> comes in:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">function toResultStream(request) {
return Bacon.fromPromise($.ajax(request))
}
availabilityResponse = availabilityRequest.flatMap(toResultStream)
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Now you'll have a new EventStream called <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">availabilityResponse</code>. What <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMap</code> does is</div>
<ol style="font-size: 15px; line-height: 25px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li>It calls your function for each value in the source stream</li>
<li>It expects your function to return a new EventStream</li>
<li>It collects the values of all created streams into the result stream</li>
</ol>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Like in this diagram.</div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
<a href="https://raw.github.com/wiki/baconjs/bacon.js/baconjs-flatmap.png" style="color: #4183c4; text-decoration: none;" target="_blank"><img alt="flatMap" src="https://raw.github.com/wiki/baconjs/bacon.js/baconjs-flatmap.png" style="border: 0px; box-sizing: border-box; max-width: 100%;" /></a></div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
So here we go. The only issue left is that <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMap</code> doesn't care about response ordering. It spits out the results in the same order as they arrive. So, it may (and will) happen that</div>
<ol style="font-size: 15px; line-height: 25px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li>Request A is sent</li>
<li>Request B is sent</li>
<li>Result of request B comes</li>
<li>Result of request A comes</li>
</ol>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
.. and your <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">availabilityResponse</code> stream will end with the wrong answer, because the latest response is not for the latest request. Fortunately there's a method for fixing this exact problem: Just replace <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMap</code> with <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">flatMapLatest</code> (previously called "switch") and you're done.</div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
<a href="https://raw.github.com/wiki/baconjs/bacon.js/baconjs-switch.png" style="color: #4183c4; text-decoration: none;" target="_blank"><img alt="switch" src="https://raw.github.com/wiki/baconjs/bacon.js/baconjs-switch.png" style="border: 0px; box-sizing: border-box; max-width: 100%;" /></a></div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Now that you know how it works, you may as well use the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">ajax()</code> method that Bacon.UI provides:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">availabilityResponse = availabilityRequest.ajax()
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
POW!</div>
<h2 style="border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; cursor: text; font-size: 2em; line-height: 1.7; margin: 1em 0px 15px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2012_11_18_Bacon_Tutorial_III.md#the-easy-part--side-effects" name="the-easy-part--side-effects" style="bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin-left: -30px; padding-left: 30px; padding-right: 6px; position: absolute; text-decoration: none; top: 0px;"></a>The easy part : side-effects</h2>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Let's show the "username not available" message. It's actually a stateful thing, so we'll convert the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">availabilityResponse</code> stream into a new Property:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">usernameAvailable = availabilityResponse.toProperty(true)
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
The boolean value is used to give the property a starting value. So now this property starts with the value <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">true</code> and after that reflects that value from the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">availabilityRequest</code> stream. The visibility of the message element should actually equal to the negation of this property. So why not something like</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">usernameAvailable.not().onValue(setVisibility, unavailabilityLabel)
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Once again, this is equivalent to</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">usernameAvailable.not().onValue(function(show) { setVisibility(unavailabilityLabel, show) })
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
... the idea in the former being that we <a href="http://en.wikipedia.org/wiki/Partial_application" style="color: #4183c4; text-decoration: none;">partially-apply</a> the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">setVisibility</code> function: Bacon will call the function with<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">unavailabilityLabel</code> fixed as the first argument. The second argument to the function will be the value from the<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">usernameAvailable.not()</code> Property.</div>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
Finally, we'll also disable the Register button when the username is unavailable. This is done by changing</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">buttonEnabled = usernameEntered.and(fullnameEntered)
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
to</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(221, 221, 221); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px; word-wrap: normal;">buttonEnabled = usernameEntered.and(fullnameEntered).and(usernameAvailable)
</code></pre>
<div style="font-size: 15px; line-height: 25px; margin-bottom: 15px; margin-top: 15px;">
That's it for now. The result code can be found in the <a href="https://github.com/raimohanska/bacon-devday-code/tree/tutorial-3" style="color: #4183c4; text-decoration: none;">tutorial-3 branch</a>.</div>
<div style="font-size: 15px; line-height: 25px; margin-top: 15px;">
Ideas for Part IV?</div>
</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com26tag:blogger.com,1999:blog-1799557918826925298.post-29123600318521477492012-11-11T14:11:00.003-08:002012-11-11T14:11:47.830-08:00Daddy, what does the "new" keyword do?<br />
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
</div>
<div style="border: 0px; margin-bottom: 15px; padding: 0px;">
Sit down, and I'll tell you. Ok, it creates a new context object for you, then you'll have a new <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">this</code> to work with. This object will be the return value of the expression starting with <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">new</code>. For example,</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">> function Dog() { this.name = "poo" }
undefined
> new Dog().name
'poo'
</code></pre>
<blockquote style="border-left-color: rgb(221, 221, 221); border-left-style: solid; border-width: 0px 0px 0px 4px; color: #777777; margin: 15px 0px; padding: 0px 15px; quotes: none;">
<div style="border: 0px; padding: 0px;">
But daddy, what happens if you accidentally forget to use <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">new</code>?</div>
</blockquote>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Um, er, lemme see.</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">> Dog().name
TypeError: Cannot read property 'name' of undefined
</code></pre>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So, your constructor gets called, but nothing is returned and your Dog is <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">undefined</code>.</div>
<blockquote style="border-left-color: rgb(221, 221, 221); border-left-style: solid; border-width: 0px 0px 0px 4px; color: #777777; margin: 15px 0px; padding: 0px 15px; quotes: none;">
<div style="border: 0px; padding: 0px;">
But what happened to the name?</div>
</blockquote>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Well, it's the name of the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Window</code> or what ever your "global object" is, depending on your execution environment. But its bed time now, good night.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
< one hour passes ></div>
<blockquote style="border-left-color: rgb(221, 221, 221); border-left-style: solid; border-width: 0px 0px 0px 4px; color: #777777; margin: 15px 0px; padding: 0px 15px; quotes: none;">
<div style="border: 0px; padding: 0px;">
Dad! I can't get no sleep! What if I return something from my Dog constructor?</div>
</blockquote>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
< starts node.js ></div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Well, if you call it with <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">new</code>, the return value is discarded. If you forget the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">new</code>, the expression evaluates to the return value.</div>
<blockquote style="border-left-color: rgb(221, 221, 221); border-left-style: solid; border-width: 0px 0px 0px 4px; color: #777777; margin: 15px 0px; padding: 0px 15px; quotes: none;">
<div style="border: 0px; padding: 0px;">
But dad, that makes no sense and can cause unexpected application errors at runtime!!</div>
</blockquote>
<div style="border: 0px; margin-top: 15px; padding: 0px;">
That's right, son. That's why daddy avoids the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">new</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">this</code> keywords. Now good night.</div>
<br />
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com11tag:blogger.com,1999:blog-1799557918826925298.post-78831558186088285252012-11-08T12:28:00.000-08:002012-11-17T14:38:48.594-08:00Bacon.js Tutorial Part II: Get Started<span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">In my</span><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"> </span><a href="http://nullzzz.blogspot.fi/2012/11/baconjs-tutorial-part-i-hacking-with.html" style="border: 0px; color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px; text-decoration: initial;">previous blog posting posting</a><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">, I introduced a Registration Form application case study, and then hacked it together with just jQuery, with appalling results. Now we shall try again, using</span><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;"> </span><a href="https://github.com/raimohanska/bacon.js" style="border: 0px; color: #4183c4; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 0px; padding: 0px; text-decoration: initial;">Bacon.js</a><span style="color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px;">. It'll take a while, of course, but the result will be quite pleasing, so please be patient. We'll start from the very basics.</span><br />
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This is how you implement an app with Bacon.js.</div>
<ol style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li style="border: 0px; margin: 0px; padding: 0px;">Capture input into EventStreams and Properties</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Transform and compose signals into ones that describe your domain.</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Assign side-effects to signals</li>
</ol>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
In practise, you'll probably pick a single feature and do steps 1..3 for that. Then pick the next feature and so on until you're done. Hopefully you'll do some refactoring on the way to keep your code clean.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Sometimes it may help to draw the thing on paper. For our case study, I've done that for you:</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
<a href="https://raw.github.com/raimohanska/bacon-devday-slides/master/images/registration-form-bacon.png" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;" target="_blank"><img alt="signals" src="https://raw.github.com/raimohanska/bacon-devday-slides/master/images/registration-form-bacon.png" style="border: 0px; margin: 0px; max-width: 100%; padding: 0px;" /></a></div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
In this diagram, the greenish boxes are EventStreams and the gray boxes are Properties. The top three boxes represent the raw input signals:</div>
<ul style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li style="border: 0px; margin: 0px; padding: 0px;">Key-up events on the two text fields</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Clicks on the register button</li>
</ul>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
In this posting we'll capture the input signals, then define the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">username</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">fullname</code> properties. In the end, we'll be able to print the values to the console. Not much, but you gotta start somewhere.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2012_11_05_Bacon_Tutorial_II.md#setup" name="setup" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Setup</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
You can just read the tutorial, or you can try things yourself too. In case you prefer the latter, here are the instructions.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
First, you should get the code skeleton on your machine.</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">git clone git@github.com:raimohanska/bacon-devday-code.git
cd bacon-devday-code
git co -t origin/clean-slate
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So now you've cloned the source code and switched to the <a href="https://github.com/raimohanska/bacon-devday-code/tree/clean-slate" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">clean-slate</a> branch. Alternatively you may consider forking the repo first and creating a new branch if you will.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Anyway, you can now open the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">index.html</code> in your browser to see the registration form. You may also open the file in your favorite editor and have a brief look. You'll find some helper variables and functions for easy access to the DOM elements.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2012_11_05_Bacon_Tutorial_II.md#capturing-input-from-dom-events" name="capturing-input-from-dom-events" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Capturing Input from DOM Events</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
Bacon.js is not a jQuery plugin or dependent on jQuery in any way. However, if it finds jQuery, it adds a method to the jQuery object prototype. This method is called <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">asEventStream</code>, and it is used to capture events into an EventStream. It's quite easy to use.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
To capture the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">keyup</code> events on the username field, you can do</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">$("#username input").asEventStream("keyup")
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
And you'll get an <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">EventStream</code> of the jQuery keyup events. Try this in your browser Javascript console:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">$("#username input").asEventStream("keyup").log()
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now the events will be logged into the console, whenever you type something to the username field (try!). To define the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">username</code>property, we'll transform this stream into a stream of textfield values (strings) and then convert it into a Property:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">$("#username input").asEventStream("keyup").map(function(event) { return $(event.target).val() }).toProperty("")
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
To see how this works in practise, just add the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">.log()</code> call to the end and you'll see the results in your console.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
What did we just do?</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
We used the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">map</code> method to transform each event into the current value of the username field. The <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">map</code> method returns another stream that contains mapped values. Actually it's just like the map function in <a href="http://underscorejs.org/" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">underscore.js</a>, but for EventStreams and Properties.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
After mapping stream values, we converted the stream into a Property by calling <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">toProperty("")</code>. The empty string is the initial value for the Property, that will be the current value until the first event in the stream. Again, the toProperty method returns a new Property, and doesn't change the source stream at all. In fact, all methods in Bacon.js return something, and most have no side-effects. That's what you'd expect from a functional programming library, wouldn't you?</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
The <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">username</code> property is in fact ready for use. Just name it and copy it to the source code:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">username = $("#username input").asEventStream("keyup").map(function(event) { return $(event.target).val() }).toProperty("")
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
I intentionally omitted "var" at this point to make it easier to play with the property in the browser developer console.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Next we could define <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">fullname</code> similarly just by copying and pasting. Shall we?</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Nope. We'll refactor to avoid duplication:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">function textFieldValue(textField) {
function value() { return textField.val() }
return textField.asEventStream("keyup").map(value).toProperty(value())
}
username = textFieldValue($("#username input"))
fullname = textFieldValue($("#fullname input"))
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Better! In fact, there's already a <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">textFieldValue</code> function available in <a href="https://github.com/raimohanska/Bacon.UI.js/blob/master/Bacon.UI.js" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: initial;">Bacon.UI</a>, and it happens to be incluced in the code already so you can just go with</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">username = Bacon.UI.textFieldValue($("#username input"))
fullname = Bacon.UI.textFieldValue($("#fullname input"))
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So, there's a helper library out there where I've shoveled some of the things that seems to repeat in different projects. Feel free to contribute!</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Anyway, if you put the code above into your source code file, reload the page in the browser and type</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">username.log()
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
to the developer console, you'll see username changes in the console log.</div>
<h2 style="-webkit-font-smoothing: antialiased; border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-width: 0px 0px 1px; cursor: text; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin: 20px 0px 10px; padding: 0px; position: relative;">
<a class="anchor" href="https://github.com/raimohanska/nulzzzblog/blob/master/2012_11_05_Bacon_Tutorial_II.md#mapping-properties-and-adding-side-effects" name="mapping-properties-and-adding-side-effects" style="border: 0px; bottom: 0px; color: #4183c4; cursor: pointer; display: block; left: 0px; margin: 0px 0px 0px -30px; padding: 0px 0px 0px 30px; position: absolute; text-decoration: initial; top: 0px;"></a>Mapping Properties and Adding Side-Effects</h2>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
To get our app to actually do something visible besides writing to the console, we'll define a couple of new <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Properties</code>, and assign our first side-effect. Which is enabling/disabling the Register button based on whether the user has entered something to both the username and fullname fields.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
I'll start by defining the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">buttonEnabled</code> Property:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">function and(a,b) { return a && b }
buttonEnabled = usernameEntered.combine(fullnameEntered, and)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So I defined the Property by combining to props together, with the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">and</code> function. The <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">combine</code> method works so that when either<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">usernameEntered</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">fullnameEntered</code> changes, the result Property will get a new value. The new value is constructed by applying the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">and</code> function to the values of both props. Easy! And can be even easier:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">buttonEnabled = usernameEntered.and(fullnameEntered)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This does the exact same thing as the previous one, but relies on the boolean-logic methods (<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">and</code>, <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">or</code>, <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">not</code>) included in Bacon.js.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
But something's still missing. We haven't defined <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">usernameEntered</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">fullnameEntered</code>. Let's do.</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">function nonEmpty(x) { return x.length > 0 }
usernameEntered = username.map(nonEmpty)
fullnameEntered = fullname.map(nonEmpty)
buttonEnabled = usernameEntered.and(fullnameEntered)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So, we used the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">map</code> method again. It's good to know that it's applicable to both <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">EventStreams</code> and <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">Properties</code>. And the<code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">nonEmpty</code> function is actually already defined in the source code, so you don't actually have to redefine it.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
The side-effect part is simple:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">buttonEnabled.onValue(function(enabled) {
$("#register button").attr("disabled", !enabled)
})
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Try it! Now the button gets immediately disabled and will enabled once you type something to both the text fields. Mission accomplished!</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
But we can do better.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
For example,</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">buttonEnabled.not().onValue($("#register button"), "attr", "disabled")
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This relies on te fact that the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">onValue</code> method, like many other Bacon.js methods, supports different sets of parameters. On of them is the above form, which can be translated as "call the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">attr</code> method of the register button and use <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">disabled</code> as the first argument". The second argument for the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">attr</code> method will be taken from the current property value.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
You could also do the same by</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">buttonEnabled.assign(setEnabled, registerButton)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now we rely on the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">setEnabled</code> function that's defined in our source code, as well as <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">registerButton</code>. The above can be translated to "call the <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">setEnabled</code> function and use <code style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px 2px; padding: 0px 5px; white-space: nowrap;">registerButton</code> as the first argument".</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So, with some Bacon magic, we eliminated the extra anonymous function and improved readability. Om nom nom.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-top: 15px; padding: 0px;">
And that's it for now. We'll do AJAX soon.</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com292tag:blogger.com,1999:blog-1799557918826925298.post-58351094141890429692012-11-04T09:47:00.001-08:002012-11-04T09:47:05.488-08:00Bacon.js Tutorial Part I : Hacking With jQuery<br />
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
</div>
<div style="border: 0px; margin-bottom: 15px; padding: 0px;">
This is the first part of a hopefully upcoming series of postings intended as a <a href="https://github.com/raimohanska/bacon.js" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: none;">Bacon.js</a> tutorial. I'll be building a fully functional, however simplified, AJAX registration form for an imaginary web site.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This material is based on my presentation/hands-on session at <a href="http://reaktordevday.fi/2012/" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: none;">Reaktor Dev Day 2012</a> where I had to squeeze a Bacon.js intro and a coding session into less than an hour. I didn't have much time to discuss the problem and jumped into the solution a bit too fast. This time I'll first try to explain the problem I'm trying to solve with Bacon. So bear with me. Or have a look at the <a href="https://github.com/raimohanska/bacon-devday-code/blob/full-solution/index.html" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: none;">Full Solution</a> first if you like.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Anyway, the registration form could look something like this:</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
<a href="https://raw.github.com/raimohanska/nulzzzblog/master/images/registration-form-ui.png" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: none;" target="_blank"><img alt="ui-sketch" src="https://raw.github.com/raimohanska/nulzzzblog/master/images/registration-form-ui.png" style="border: 0px; margin: 0px; max-width: 100%; padding: 0px;" /></a></div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
This seems ridiculously simple, right? Enter username, fullname, click and you're done. As in</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;"> registerButton.click(function(event) {
event.preventDefault()
var data = { username: usernameField.val(), fullname: fullnameField.val()}
$.ajax({
type: "post",
url: "/register",
data: JSON.stringify(data)
})
})
</code></pre>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
At first it might seem so, but if you're planning on implementing a top-notch form, you'll want to consider including</div>
<ol style="border: 0px; margin: 15px 0px; padding: 0px 0px 0px 30px;">
<li style="border: 0px; margin: 0px; padding: 0px;">Username availability checking while the user is still typing the username</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Showing feedback on unavailable username</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Showing an AJAX indicator while this check is being performed</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Disabling the Register button until both username and fullname have been entered</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Disabling the Register button in case the username is unavailable</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Disabling the Register button while the check is being performed</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Disabling the Register button immediately when pressed to prevent double-submit</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Showing an AJAX indicator while registration is being processed</li>
<li style="border: 0px; margin: 0px; padding: 0px;">Showing feedback after registration</li>
</ol>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Some requirements, huh? Still, all of these sound quite reasonable, at least to me. I'd even say that this is quite standard stuff nowadays. You might now model the UI like this:</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
<a href="https://raw.github.com/raimohanska/bacon-devday-slides/master/images/registration-form-thorough.png" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: none;" target="_blank"><img alt="dependencies" src="https://raw.github.com/raimohanska/bacon-devday-slides/master/images/registration-form-thorough.png" style="border: 0px; margin: 0px; max-width: 100%; padding: 0px;" /></a></div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now you see that, for instance, enabling/disabling the Register button depends on quite a many different things, some of them asynchronous. But hey, fuck the shit. Let's just hack it together now, right? Some jQuery and we're done in a while.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
[hack hack hack] ... k, done.</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;"> var usernameAvailable, checkingAvailability, clicked
usernameField.keyup(function(event) {
showUsernameAjaxIndicator(true)
updateButtonState()
$.ajax({ url : "/usernameavailable/" + usernameField.val()}).done(function(available) {
usernameAvailable = available
setVisibility(unavailabilityLabel, !available)
showUsernameAjaxIndicator(false)
updateButtonState()
})
})
fullnameField.keyup(updateButtonState)
registerButton.click(function(event) {
event.preventDefault()
clicked = true
setVisibility(registerAjaxIndicator, true)
updateButtonState()
var data = { username: usernameField.val(), fullname: fullnameField.val()}
$.ajax({
type: "post",
url: "/register",
data: JSON.stringify(data)
}).done(function() {
setVisibility(registerAjaxIndicator, false)
resultSpan.text("Thanks!")
})
})
updateButtonState()
function showUsernameAjaxIndicator(show) {
checkingAvailability = show
setVisibility(usernameAjaxIndicator, show)
}
function updateButtonState() {
setEnabled(registerButton, usernameAvailable
&& nonEmpty(usernameField.val())
&& nonEmpty(fullnameField.val())
&& !checkingAvailability
&& !clicked)
}
</code></pre>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Beautiful? Nope, could be even uglier though. Works? Seems to. Number of variables? 3.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Unfortunately, there's still a major bug in the code: the username availability responses may return in a different order than they were requested, in which case the code may end up showing an incorrect result. Easy to fix? Well, kinda.. Just add a counter and .. Oh, it's sending tons of requests even if you just move the cursor with the arrow keys in the username field. Hmm.. One more variable and.. Still too many requests... Throttling needed... It's starting to get a bit complicated now... Oh, setTimeout, clearTimeout... DONE.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Here's the code now:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;"> var usernameAvailable, checkingAvailability, clicked, previousUsername, timeout
var counter = 0
usernameField.keyup(function(event) {
var username = usernameField.val()
if (username != previousUsername) {
if (timeout) {
clearTimeout(timeout)
}
previousUsername = username
timeout = setTimeout(function() {
showUsernameAjaxIndicator(true)
updateButtonState()
var id = ++counter
$.ajax({ url : "/usernameavailable/" + username}).done(function(available) {
if (id == counter) {
usernameAvailable = available
setVisibility(unavailabilityLabel, !available)
showUsernameAjaxIndicator(false)
updateButtonState()
}
})
}, 300)
}
})
fullnameField.keyup(updateButtonState)
registerButton.click(function(event) {
event.preventDefault()
clicked = true
setVisibility(registerAjaxIndicator, true)
updateButtonState()
var data = { username: usernameField.val(), fullname: fullnameField.val()}
$.ajax({
type: "post",
url: "/register",
data: JSON.stringify(data)
}).done(function() {
setVisibility(registerAjaxIndicator, false)
resultSpan.text("Thanks!")
})
})
updateButtonState()
function showUsernameAjaxIndicator(show) {
checkingAvailability = show
setVisibility(usernameAjaxIndicator, show)
}
function updateButtonState() {
setEnabled(registerButton, usernameAvailable
&& nonEmpty(usernameField.val())
&& nonEmpty(fullnameField.val())
&& !checkingAvailability
&& !clicked)
}
</code></pre>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Number of variables: 6 Max. level of nesting: 5</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Are your eyes burning already?</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Writing this kind of code is like changing diapers. Except kids grow up and change your diapers in the end. This kind of code just grows uglier and more disgusting and harder to maintain. It's like if your kids gradually started to... Well, let's not go there.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
How to improve this code? With MVC frameworks. Nope. Object-oriented design? Maybe. You'll end up with more code and better structure, but iIt will still be hard to separate concerns cleanly...</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
No matter what, you'll need to store the UI state, like whether or not an AJAX request is pending, somewhere. And you need to trigger things like enabling/disabling the button somewhere, and usually in many places, as in the code above. This introduces dependencies in all the wrong places. Now many different parts of code need to know about updating the status of the button, while it should be the other way around.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
With the well-known Observer pattern (say, jQuery custom events) you can do some decoupling, so that you'll have an Observer that observes many events and then updates the button state. But, this does not solve the problem of providing the updateButtonState function with all the relevant data. So you'll end up using one mechanism for triggering state update and another one for maintaining required mutable state. No good.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Wouldn't it be great if you had some abstraction for a signal that you can observe and compose, so that the "button enabled" state would be a composite signal constructed from all the required input signals?</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Say yes.</div>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Good. The Property class in Bacon.js is just that: a composable signal representing the state of something. The EventStream class is a composable signal representing distinct events. Define the following signals:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">var username = ..
var fullname = ..
var buttonClick = ..
</code></pre>
<div style="border: 0px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
The rest is just composition.</div>
<div style="border: 0px; margin-top: 15px; padding: 0px;">
But hey, I'll get to that in the next posting.</div>
<br />
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com548tag:blogger.com,1999:blog-1799557918826925298.post-7581382003394283012012-11-02T23:48:00.003-07:002012-11-02T23:48:20.107-07:00Back from Callback Hell with Bacon.js<br />
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; padding: 0px;">
I've heard the words Callback Hell quite a many times lately. I'll take it that you know what I'm talking about. Just gonna briefly show how to get Back from Hell with <a href="https://github.com/raimohanska/bacon.js" style="border: 0px; color: #4183c4; margin: 0px; padding: 0px; text-decoration: none;">bacon.js</a>...</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
So, what about if you want to perform two independent AJAX calls and do something with the values of both? You might</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">$.ajax("/cats").done(function(cats) {
$.ajax("/dogs").done(function(dogs) {
doStuffWithCatsAndDogs(cats, dogs)
})
})
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
That's a minor callback hell allready. How about</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">var cats = Bacon.fromPromise($.ajax("/cats")
var dogs = Bacon.fromPromise($.ajax("/dogs")
Bacon.combineAsArray(cats, dogs).onValues(doStuffWithCatsAndDogs)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Know what? Your AJAX just got parallel, too. Maybe we should refactor this too:</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">function ajax(url) { return Bacon.fromPromise($.ajax(url)) }
Bacon.combineAsArray(ajax("/cats"), ajax("/dogs")).onValues(doStuffWithCatsAndDogs)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
That's it.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now if you wanted both cats and dogs and then do something with both <em style="border: 0px; margin: 0px; padding: 0px;">and</em> something based on an AJAX query triggered by user input... You would do</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">$.ajax("/cats").done(function(cats) {
$.ajax("/dogs").done(function(dogs) {
$("input.name").onKeyUp(function() {
$.ajax("/stuff/" + $("input.name").val()).done(function(stuff) {
var allTheStuff = {
cats: cats,
dogs: dogs,
stuff: stuff
}
doStuff(allTheStuff)
})
})
})
})
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
That's some serious callback hell. See any problems? For one, the user input won't be captured until both AJAX calls (cats, dogs) are completed, and they are not done in parallel, either. Further, the AJAX calls triggered by user input do not necessarily return in the same order as they are issued, so the last call to "doStuff" might be based on stale data. That might prove hard to fix?</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
With Bacon, you could do</div>
<pre style="background-color: #f8f8f8; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid rgb(204, 204, 204); color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 13px; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow: auto; padding: 6px 10px;"><code style="background-color: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: none; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; margin: 0px; padding: 0px;">function ajax(url) { return Bacon.fromPromise($.ajax(url)) }
function urlForStuff(name) { return "/stuff/" + name }
var name = Bacon.UI.textFieldValue($("input.name"))
Bacon.combineTemplate({
cats: ajax("/cats"),
dogs: ajax("/dogs",
stuff: name.map(urlForStuff).ajax()
}).onValue(doStuff)
</code></pre>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding: 0px;">
Now you'll start capturing user input immediately. Your initial AJAX calls are executed in parallel and combined with the latest value returned by the "/stuff/*" AJAX when all values are available.</div>
<div style="border: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-top: 15px; padding: 0px;">
And the ".ajax()" call actually ensures that the results are based on latest user input.</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com11tag:blogger.com,1999:blog-1799557918826925298.post-18026015046096179302012-03-10T15:33:00.002-08:002012-03-10T23:22:06.346-08:00Introducing Bacon.js<br />
<h1 style="-webkit-font-smoothing: antialiased; background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<span style="background-color: white; color: #333333; line-height: 22px;">In my previous posting I ranted a bit about the WTFs related to RxJs hot and cold Observables and didn't come up with a very good workaround.</span></h1>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
This time I have one. Use <a href="https://github.com/raimohanska/bacon.js" style="border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #4183c4; font-size: 14px; font: inherit; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">Bacon.js</a>. That would be my homemade reactive library for Javascript. It solves the hot/cold problem by replacing Observable with two distinct abstractions:</div>
<ul style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; padding-bottom: 0px; padding-left: 30px; padding-right: 0px; padding-top: 0px;">
<li style="border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-size: 14px; font: inherit; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">EventStream: an observable stream of events. Never emits the same event twice. Always emits the same event to all subscribers.</li>
<li style="border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-size: 14px; font: inherit; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Property: like a stream with current value. Any new subscriber will immediately be called back with the current value if there is any.</li>
</ul>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
I'll use my earlier game development examples again, converted into bacon.js, to show how the new library works. I assume some RxJs knowhow.</div>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
So if you have some events (say keyboard events) and you want to map them into some domain events (like game character movement), you can start with the source events and use <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">map</code>, <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">filter</code> and <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">merge</code>.<br />
The following functions allow you to create a keyUp or keyDown stream for a given keyCode.</div>
<pre style="background-color: white; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; color: #333333; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-style: initial; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><span style="font-family: 'Courier New', Courier, monospace;">var allKeyUps = $(document).asEventStream("keyup")
var allKeyDowns = $(document).asEventStream("keydown")
function always(value) { return function(_) { return value } }
function keyCodeIs(keyCode) { return function(event) { return event.keyCode == keyCode} }
function keyUps(keyCode) { return allKeyUps.filter(keyCodeIs(keyCode)) }
function keyDowns(keyCode) { return allKeyDowns.filter(keyCodeIs(keyCode)) }</span>
</code></pre>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
When you want to accumulate some state (like player position), you can use <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">toProperty</code> or <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">scan</code> to get a stateful Property. These things have a meaningful "current value", unlike streams like "keyup events".</div>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The following creates keyState property from keyups and keydowns of each arrow key, then combines them into a single "direction" property usin <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">toProperty</code>:</div>
<pre style="background-color: white; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; color: #333333; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-style: initial; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><span style="font-family: 'Courier New', Courier, monospace;">function concat(a1, a2) {
return a1.concat(a2)
}
function keyState(keyCode, value) {
return keyDowns(keyCode).map(always([value])).
merge(keyUps(keyCode).map(always([]))).toProperty([])
}
var direction = keyState(38, new Vector2D(0, -1))
.combine(keyState(40, new Vector2D(0, 1)), concat)
.combine(keyState(37, new Vector2D(-1, 0)), concat)
.combine(keyState(39, new Vector2D(1, 0)), concat)
.map(head)</span>
</code></pre>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The direction property will start with the empty array []. When you press an arrow key, the value will change to an array containing the corresponding direction vector. The <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">combine</code> method of Property is used to combine multiple properties into one.</div>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The <code style="background-color: #f8f8f8; border-bottom-color: rgb(234, 234, 234); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(234, 234, 234); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(234, 234, 234); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(234, 234, 234); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; white-space: nowrap;">Property.sample</code> method can be used to sample the current value of a Property each 50 milliseconds. The result will be an EventStream, because these are now distincts events (no current value, see?). So if you sample the direction each 50 milliseconds, you'll get a "movements" stream:</div>
<pre style="background-color: white; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; color: #333333; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-style: initial; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><span style="font-family: 'Courier New', Courier, monospace;">var startPos = new Vector2D(50, 50)
function head(array) { return array[0] }
function id(x) { return x }
var movements = direction.sample(50).filter(id)</span>
</code></pre>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Now we can further accumulate the movements into the current position:</div>
<pre style="background-color: white; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-color: initial; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; color: #333333; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: 19px; margin-bottom: 15px; margin-top: 15px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-style: initial; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font: inherit; line-height: normal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><span style="font-family: 'Courier New', Courier, monospace;">var position = movements
.scan(startPos, function(pos, move) { return pos.add(move) })
</span></code></pre>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
And voila, we are able to control our game character with the keyboard.</div>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
For a live demo of the same, see my <a href="http://raimohanska.github.com/bacon.js-slides" style="border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #4183c4; font-size: 14px; font: inherit; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">slideshow</a>, especially the <a href="http://raimohanska.github.com/bacon.js-slides/6.html" style="border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #4183c4; font-size: 14px; font: inherit; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">last slide</a>.</div>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-bottom: 15px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Bacon.js has quite good test coverage, it's open-source, it has at least some level of documentation and I'm a nice guy, so please try it out and let me know how the Bacon suits you.</div>
<div style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #333333; font-family: Helvetica, arial, freesans, clean, sans-serif; font: inherit; line-height: 22px; margin-top: 15px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
All help is also appreciated also, so please contribute.</div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com4tag:blogger.com,1999:blog-1799557918826925298.post-22712809724613137812012-01-26T13:14:00.001-08:002012-01-26T13:15:24.991-08:00Things You Should Know About RX<br />
<h1 style="background-color: white; border-bottom-width: 0px; border-color: initial; border-image: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 24px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<span style="font-size: 14px; font-weight: normal; line-height: 22px;">As I've said before, RX for Javascript is a great library for event-rich Javascript apps. But as with many tools of great power, it allows you to do stupid things and get hurt. In this posting I'm focusing on those features that will probably provide you with the most WTFs.</span></h1>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Some examples</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
A counter that increases or decreases its value based on clicks to <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">+</code> and <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">-</code> buttons can be implemented like this:</div>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; border-bottom-style: none; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; line-height: 19px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">incr</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">$</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s1" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">'#incr'</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">toObservable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s1" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">'click'</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Select</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">always</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">1</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">))</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">decr</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">$</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s1" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">'#decr'</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">toObservable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s1" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">'click'</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Select</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">always</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">-</span><span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">1</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">))</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">series</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">incr</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Merge</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">decr</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span>
<span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Scan</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">0</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">function</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">total</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">x</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">return</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">total</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">+</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">x</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">})</span>
</pre>
</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Is good? Well, as long as you subscribe at the right time. If you subscribe later, the later subscriber will get different results from the first one, because it's counter will start from 0 when it subscribes.</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
You could derive a <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">position</code> stream from a <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">movements</code> stream as follows:</div>
<pre style="background-color: white; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; line-height: 19px; margin-top: 10px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"> var position = movements
.Scan(startPos, function(pos, move) { return pos.add(move) })
.StartWith(startPos)
</code></pre>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Is good? Well, the exact same problem. Position stream delivers different results depending when you subscribe.</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Both examples seem perfectly ok, unless you know the details of RX. Let's take a walk.</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Misconception #1 : Observable Is a Stream of Events</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Well, I have said the above sentence countless times, regardless of how wrong it is. Actually Rx would make a lot more sense (with less WTFs) if that were true. A real stream, in my opinion, would provide all of its Subscribers with the same series of events after any moment of time <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">t</code>. Unfortunately with RX, the events that you receive after <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">t</code> depends on when you subscribed.</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Why?</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Hot and Cold Observables</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
In RX, there are "hot" and "cold" observables. As Bnaya Eshet described in his <a href="http://blogs.microsoft.co.il/blogs/bnaya/archive/2010/03/13/rx-for-beginners-part-9-hot-vs-cold-observable.aspx" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">blog posting</a>:</div>
<blockquote style="background-color: white; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 4px; color: #555555; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 14px; margin-left: 0px; margin-right: 0px; margin-top: 14px; padding-bottom: 0px; padding-left: 11px; padding-right: 0px; padding-top: 0px;">
<div style="margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
If a tree falls in a forest and no one is around to hear it, does it make a sound? if it do make a sound when nobody observed it, we should mark it as hot, otherwise it should be marked as cold.</div>
</blockquote>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
So what? Well, simply put, hot observables are consistent among subscribers and cold ones are not. A hot observable can be seen as a list of <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">(time, value)</code> pairs. A hot observable won't produce the an event to subscribers that subscribed after the event occurred. The cold ones are, well, something that depends on when you subscribe.</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
All Observables that are directly derived from some real-world events are hot. For instance, mouse and keyboard events will be consistent among all subscribers. But, if you combine them with a cold observable, or apply a stateful combinator, such as <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Scan</code>, you're not so safe anymore.</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Observables from Arrays</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Observable.FromArray</code> serves you a cold Observable. It always spits out the same list of objects when you subscribe. Doesn't provide a consistent mapping of time to events. Period.</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Mixing Hot and Cold</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
If you mix hot with cold, what do you get? Medium? So, if you</div>
<pre style="background-color: white; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; line-height: 19px; margin-top: 10px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"><code style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: none; border-color: initial; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: none; border-width: initial; border-width: initial; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var hotness = $(document).toObservable("keyup")
var temperature = Observable.FromArray("coldness").Concat(hotness.Select(always("hotness")))
</code></pre>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
You might expect to get an observable starting with "coldness" and producing "hotness" at each keyup. However, <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">StartWith</code> made your new observable a bit colder in the sense that any new subscriber will always get "coldness" first.</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
No matter how you combine coldness with hotness, you won't get a hot Observable back. It won't be cold as in "tree falling in the woods", but inconsistent anyway. Lukewarm? No, more like Groundhog Day.</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Stateful Streams Using Scan</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
As I showed in the counter-example (pun intended) above, Scan will give you a Groundhog Day Observable.</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
StartWith</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Using <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">StartWith</code> won't save you. It's the same thing as concatenating with a cold stream of one event.</div>
<h2 style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 18px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 20px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Workaround : Publish/Connect</h2>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
This is a workaround I already presented in my hot-tempered <a href="http://nullzzz.blogspot.com/2011/02/failing-with-rx-js.html" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">Failing with RX-JS</a> posting. You can make your Observable hot again by using <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Publish</code> and <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Connect</code>. For instance, you can fix the broken counter like this:</div>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; border-bottom-style: none; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font-size: 13px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; line-height: 19px; overflow-x: auto; overflow-y: auto; padding-bottom: 6px; padding-left: 10px; padding-right: 10px; padding-top: 6px;"> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">series</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">incr</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Merge</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">decr</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span>
<span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Scan</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">0</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">function</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">total</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">x</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">return</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">total</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">+</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">x</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">})</span>
<span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Publish</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">()</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">dispose</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">series</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Connect</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">()</span>
</pre>
</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1em; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
So first you call <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Publish</code> on your Observable. That'll give you an Observable, that will have a single connection to the underlying Observable, no matter how many subscribers you add. Then you call <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Connect</code>, which will start it, by calling the <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">Subscribe</code> method of the underlying stream. It will also give you a back a "dispose" function for disconnecting.</div>
<div style="background-color: white; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 14px; line-height: 22px; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The obvious drawback of this method is that there's more clutter. Also, the connection to the underlying stream won't be automatically disconnected. You have to call <code style="background-color: #f8f8f8; border-bottom-color: rgb(204, 204, 204); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 2px; margin-right: 2px; margin-top: 0px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; padding-top: 2px; white-space: nowrap;">dispose</code> yourself.</div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com1tag:blogger.com,1999:blog-1799557918826925298.post-47340181304658726552011-12-18T11:46:00.000-08:002011-12-18T11:46:16.392-08:00Shake your Rump!<br />
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
This time it's not about Rx for Javascript or even Javascript. Well, that's at least partly because I got hooked on Haskell and have spent most of my free time (not much though) on Haskell projects.</div>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
But lately, I've been working on a mobile (Android/iOS) app called Karma. The app will answer the ever-crucial question "who's turn is it to buy the next round?". To make it fun and easy to use, it needed an easy way of telling the system that two guys, say Bob and Mike, have just met for some beers. Thanks to location and acceleration info, it's possible to make this pairing happen just by shaking phones together.</div>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The topic of this post is the "shake to pair" technology included. We started by trying the <a href="http://bu.mp/" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">Bump</a> API. However, we found it quite unreliable, even though the actual Bump application seemed solid. I guess they don't give their best performance for API users?</div>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Now, what would a proper nerd do? What the heck, let's write it from scratch. And POW, here it is:</div>
<ul style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; margin-bottom: 1em !important; margin-left: 2em !important; margin-right: 0px !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><a href="https://github.com/raimohanska/rump" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">RUMP Server</a></li>
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><a href="https://github.com/raimohanska/rump-android" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">RUMP Android Client</a></li>
</ul>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
As you see, it's open-source. And, it's actually very simple:</div>
<ol style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; margin-bottom: 1em !important; margin-left: 2em !important; margin-right: 0px !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">When user shakes the phone, the RUMP Client sends a message to the server. Like this:</li>
</ol>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: #f8f8f8; background-image: initial; background-origin: initial; border-bottom-style: none; border-color: initial; border-image: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="-webkit-box-shadow: rgba(0, 0, 0, 0.0625) 0px 1px 2px inset; background-color: #eeeeee; border-bottom-color: rgb(221, 221, 221); border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-bottom-style: solid; border-bottom-width: 1px; border-image: initial; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(221, 221, 221); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(221, 221, 221); border-top-left-radius: 3px; border-top-right-radius: 3px; border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 12px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 1em; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px;"> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"userId"</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">:</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"john"</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"displayName"</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">:</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"John Kennedy"</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"location"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">:</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"latitude"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">:</span> <span class="mf" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">51.0</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s2" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"longitude"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">:</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">-</span><span class="mf" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">0.1</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span>
</pre>
</div>
<ol style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; margin-bottom: 1em !important; margin-left: 2em !important; margin-right: 0px !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Server matches incoming requests by time (3 second time window to match) and location (1 kilometer max distance).</div>
</li>
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
After 3 seconds from the first received request, the server sends match info to all clients that were matched with the first one. The response is just an array of all the matching requests.</div>
</li>
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Rump Client delivers the match info to the client application.</div>
</li>
<li style="margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Client application does something cool. For instance, the Karma application joins the matched users into the same table and calculates the next buyer.</div>
</li>
</ol>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Well, that's it. On the Github pages, you'll find instructions for building, running and integrating the Android Client into your app.</div>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
I think this would provide a nice basis for matching users in a multiplayer game, for instance. Shake your phones to start multi-player, anyone?</div>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
What else? We have an iPhone version of Karma in the works. Mayhaps we'll also extract the Rump client part from there and open-source that too? Would it make sense?</div>
<div style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The server-side piece is written in Haskell. I'm on a horse.</div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com0tag:blogger.com,1999:blog-1799557918826925298.post-48539069687670276792011-09-07T10:39:00.000-07:002011-09-07T10:54:04.531-07:00Zipping With Arrays in RxJs<span class="Apple-style-span" style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px; line-height: 19px;">Have you ever tried to zip a hot observable with a cold one? That's what happens below.</span><span class="Apple-style-span" style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px;"></span><br />
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="-webkit-box-shadow: rgba(0, 0, 0, 0.0625) 0px 1px 2px inset; background-color: #eeeeee; border-bottom-color: rgb(221, 221, 221); border-bottom-left-radius: 3px 3px; border-bottom-right-radius: 3px 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(221, 221, 221); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(221, 221, 221); border-top-left-radius: 3px 3px; border-top-right-radius: 3px 3px; border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 12px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 1em; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px;"> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">former</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">function</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">a</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">b</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">return</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">a</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">latter</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">function</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">a</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">b</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">return</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">b</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">logger</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">function</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">a</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">console</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">log</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">a</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">numbers</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Rx</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Observable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">FromArray</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">([</span><span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">1</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">2</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">3</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">])</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">timer</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Rx</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Observable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Interval</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">100</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span>
<span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">var</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">zipped</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">numbers</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Zip</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">timer</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">former</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span>
<span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">zipped</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Subscribe</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">logger</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span>
</pre>
</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
The idea is to print out the contents of an array to the console one-by-one, each 100 milliseconds. It doesn't work though. What I've heard is that it fails because of a bug in Rx. Instead of printing out numbers 1,2,3 to the console, the code does ... nothing.</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
A simple workaround (Thanks <a href="http://twitter.com/#!/jvesala" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">Jussi</a>) seems to work, though: Just concat your array observable with <code style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: solid !important; border-bottom-width: 1px !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: solid !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: solid !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: solid !important; border-top-width: 1px !important; color: rgb(68, 68, 68) !important; font-size: 12px !important; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px !important; padding-left: 0.2em !important; padding-right: 0.2em !important; padding-top: 0px !important;">Rx.Observable.Never</code>!</div>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="-webkit-box-shadow: rgba(0, 0, 0, 0.0625) 0px 1px 2px inset; background-color: #eeeeee; border-bottom-color: rgb(221, 221, 221); border-bottom-left-radius: 3px 3px; border-bottom-right-radius: 3px 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(221, 221, 221); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(221, 221, 221); border-top-left-radius: 3px 3px; border-top-right-radius: 3px 3px; border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 12px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 1em; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px;"> <span class="c1" style="color: #999988; font-style: italic; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">// ...</span>
<span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">numbers</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Concat</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Rx</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Observable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Never</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">()).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Zip</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">timer</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">former</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Subscribe</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">logger</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span>
<span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">1</span>
<span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">2</span>
<span class="mi" style="color: #009999; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">3</span>
</pre>
</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
So, now it prints the stuff to the console as you'd expect.</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
To make zipping with arrays a bit easier, you could add a <code style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: solid !important; border-bottom-width: 1px !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: solid !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: solid !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: solid !important; border-top-width: 1px !important; color: rgb(68, 68, 68) !important; font-size: 12px !important; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px !important; padding-left: 0.2em !important; padding-right: 0.2em !important; padding-top: 0px !important;">ZipWithArray</code> method to the <code style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: solid !important; border-bottom-width: 1px !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: solid !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: solid !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: solid !important; border-top-width: 1px !important; color: rgb(68, 68, 68) !important; font-size: 12px !important; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px !important; padding-left: 0.2em !important; padding-right: 0.2em !important; padding-top: 0px !important;">Observable</code> prototype like this.</div>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="-webkit-box-shadow: rgba(0, 0, 0, 0.0625) 0px 1px 2px inset; background-color: #eeeeee; border-bottom-color: rgb(221, 221, 221); border-bottom-left-radius: 3px 3px; border-bottom-right-radius: 3px 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(221, 221, 221); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(221, 221, 221); border-top-left-radius: 3px 3px; border-top-right-radius: 3px 3px; border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 12px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 1em; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px;"> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Rx</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Observable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">prototype</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">ZipWithArray</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="kd" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">function</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">array</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">selector</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">)</span> <span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span><span class="err" style="background-color: #e3d2d2; color: #a61717; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">·</span>
<span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">return</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">this</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Zip</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Rx</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Observable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">FromArray</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">array</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">).</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Concat</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Rx</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Observable</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="nx" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Never</span><span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">()),</span>
<span class="p" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span>
</pre>
</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
I've put this extension, as well a bunch of others on Github, in my <a href="http://github.com/raimohanska/rxjs-extensions/blob/master/zip-with-array.js" style="color: #4183c4; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none;">rxjs-extensions</a> project.</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com1tag:blogger.com,1999:blog-1799557918826925298.post-86705944445900926122011-09-06T04:02:00.000-07:002011-09-07T10:54:24.770-07:00A Nice Java HashMap Antipattern<span class="Apple-style-span" style="background-color: #f8f8f8; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 13px;"></span><br />
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Don't you just love how simple it is to instantiate maps in Java? Like, could it be any uglier than</div>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="-webkit-box-shadow: rgba(0, 0, 0, 0.0625) 0px 1px 2px inset; background-color: #eeeeee; border-bottom-color: rgb(221, 221, 221); border-bottom-left-radius: 3px 3px; border-bottom-right-radius: 3px 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(221, 221, 221); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(221, 221, 221); border-top-left-radius: 3px 3px; border-top-right-radius: 3px 3px; border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 12px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 1em; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px;"><span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Map</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><</span><span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">></span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">map</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">new</span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">HashMap</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><</span><span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">>();</span>
<span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">map</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="na" style="color: teal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">put</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"fruit"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"banana"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">);</span>
<span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">map</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">.</span><span class="na" style="color: teal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">put</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"vegetable"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"carrot"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">);</span>
</pre>
</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
I saw someone using a pattern that made it look a bit nicer, like</div>
<div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
<pre style="-webkit-box-shadow: rgba(0, 0, 0, 0.0625) 0px 1px 2px inset; background-color: #eeeeee; border-bottom-color: rgb(221, 221, 221); border-bottom-left-radius: 3px 3px; border-bottom-right-radius: 3px 3px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(221, 221, 221); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(221, 221, 221); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(221, 221, 221); border-top-left-radius: 3px 3px; border-top-right-radius: 3px 3px; border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 12px; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 1em; margin-top: 1em; overflow-x: auto; overflow-y: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px;"><span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">Map</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><</span><span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">></span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">map</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">=</span> <span class="k" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">new</span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">HashMap</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"><</span><span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">String</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">>()</span> <span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">{</span>
<span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">put</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"fruit"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"banana"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">);</span>
<span class="n" style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">put</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">(</span><span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"vegetable"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">,</span> <span class="s" style="color: #dd1144; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">"carrot"</span><span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">);</span>
<span class="o" style="font-weight: bold; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">}</span>
</pre>
</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
It kinda puts a bit more structure to this mess. However, you'll get some unwanted runtime side-effects.</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
When you use this "syntax", you'll actually create a new anonymous inner class. Each anonymous inner class instace will have a runtime reference to the<code style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: solid !important; border-bottom-width: 1px !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: solid !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: solid !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: solid !important; border-top-width: 1px !important; color: rgb(68, 68, 68) !important; font-size: 12px !important; font: normal normal normal 12px/normal 'Bitstream Vera Sans Mono', Courier, monospace; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px !important; padding-left: 0.2em !important; padding-right: 0.2em !important; padding-top: 0px !important;">this</code> object for the context where you define the anonymous inner class in. Thus, if your this happens to have references to 100 megabytes of porn, that stuff will not be GC'd as long as this Map or yours is alive. Also, should you try to serialize your map for sending or storing it somewhere, you'll also be sending your 100 megabytes of porn.</div>
<div style="line-height: 1.5em !important; margin-bottom: 1em !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;">
Enjoy your cup of Java!</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com2tag:blogger.com,1999:blog-1799557918826925298.post-31267522474352497572011-03-21T04:45:00.000-07:002011-03-24T06:46:29.534-07:00Socket.IO and Asynchronous Testing with node.js<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">This is a story about an application we wrote. If you're interested in node.js, automatic testing or Socket.IO, read on. I concentrate mostly on the testing part here though. Maybe another post on juicy node.js and Socket.IO setup later..</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;">The Problem</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">A while ago I and <a href="https://github.com/codeflows">Jari</a> got the opportunity to implement a small piece of software without any limitations on which tools to use. The assignment was to create a central server for an algorithm competition where teams have to implement a server that solves certain variants of <a href="http://en.wikipedia.org/wiki/Knapsack_problem">Knapsack</a> problems. The idea is that during a <i>competion round</i>, the competion server sends a bunch of <i>challenges</i> to the contestant servers and they have to produce a response in a given time. Each challenge is a Knapsack problem represented in a JSON message. If a contestant fails to produce a result in the given time, it is disqualified. Successful results are ranked and scored. The central server also visualizes the competition in real time by sending events to a web-based visualization client.</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;">The Technology</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">We chose to use node.js for the server and HTML+CSS+JQuery for the visualization client, at least to start with. It turned out that this was a good match and we didn't have to go for Flash or such on the client side. To get the real-time updates to the client, we decided to give <a href="http://socket.io/">Socket.IO</a> a try. And it was a good bet too. As supporters of <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a>, we decided to do the whole thing test-driven, and to do most of the testing using integration tests. As a result, we had quite a complete integration test suite for the whole thing, without faking or mocking any part of the server. And the test suite took just 200 milliseconds to run, thanks to the asynchronous testing framework provided by <a href="http://vowsjs.org/">Vows</a>.</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">All this technology made us feel like kids in a candy store. Still, we spent most of our time working on the actual problem and not on accidential complexity like persistence, serialization or concurrency. Here's the stuff that made us happy:</span><br />
<br />
<ul><li><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><a href="http://nodejs.org/">Node.js</a> makes concurrent programming a non-issue by removing all concurrency in actual application code and making all "system" calls asynchronous. No need to think about thread-pools, locking, deadlocks or the treacherous Java Memory Model.</span></li>
<li><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Node.js also makes it very easy to create HTTP clients and servers.</span></li>
<li><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><a href="http://vowsjs.org/">Vows</a> makes testing of asynchronous things easy. No need to sleep 5 seconds before checking if something has happened. Instead, use a callback to react to the thing you're waiting for to happen.</span></li>
<li><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><a href="http://socket.io/">Socket.IO</a> allows you to send messages between your browser client and your node.js server, both ways. </span></li>
<li><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Using JSON in all communications makes serialization a non-issue. Both node.js and JQuery have JSON serialization, so we just stringify() and parse() our objects and be happy.</span></li>
</ul><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;">TDD</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">I hate bugs, debugging and testing. That's why I always strive to go test-first. Especially when working with new technology. Even more so when working with a dynamic language like Javascript where there's no compiler going to save my ass. Lately I've been working with <a href="http://www.haskell.org/haskellwiki/Why_Haskell_just_works">Haskell</a> too, where there program usually works if I get it to compile.. </span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Anyways, some things are easier to test than others and sometimes it may be practical to draw a line between what's test-driven and what's not. Here we had the prototype of an easily testable piece of software: a server that communicates in JSON with other servers, producing JSON events for a user interface client. So, we decided to fake the contender servers and the UI and test the whole thing from the outside. </span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">So, let's fake. I mean, let's create fake instances of the contender servers, as well as the visualization client. Then, we'll build a test suite that uses these fakes to test-drive the actual server we're developing. I'll paste some slightly simplified pieces of code here for example. If you are really interested in the stuff, you'll find it all on <a href="https://github.com/reaktor/competition">Github</a>.</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;">Faking the Contender Servers</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Faking the contender servers was easy, as it only takes a few lines of code to create an HTTP server and have it reply to the challenges from the central server. (see <a href="https://github.com/reaktor/competition/blob/master/test/contender.js">contender.js</a>) Here's the beef:</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.start = function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.server = http.createServer(this.handler);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.server.listen(this.port, this.host);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return this;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> };</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> this.handler = function (req, res) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> extractContent(req, function(challengeJson) {</span><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var challenge = JSON.parse(challengeJson);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> testClient.challenges.push(challenge);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var result = resultStrategy(challenge)</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var resultJson = JSON.stringify(result);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> res.writeHead(200);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> res.write(resultJson);</span></div><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> res.end();</span></div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> };</span><br />
<div style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</div><div style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">So, we create an HTTP server with a given handler function that parses the input JSON into <i>challenge</i>, then saves the challenge for later inspection and finally replies using a given <i>resultStrategy</i>. The <i>extractContent </i>function is a helper that consumes all input from the HTTP request and calls a callback when done (see <a href="https://github.com/reaktor/competition/blob/master/src/contentextractor.js">contentextractor.js</a>):</div><div style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</div><div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> extractContent = function(response, contentHandler, encoding) {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (encoding) response.setEncoding(encoding);</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var content = "";</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> response.on('data', function (chunk) {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> content += chunk;</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> response.on('end', function() {</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> contentHandler(content)</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span></div><div><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span></div></div></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;">Faking the Socket.IO Client</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Faking the visualization UI was a bit harder, as Socket.IO does not provide a client library for node.js, where we run our tests. Only a browser client is included. We quickly found <a href="https://github.com/pgriess/node-websocket-client">Websocket Client</a>, but ran into trouble pretty soon because there were no suitable examples for us. But after some guesswork we figured out how to set up a client and have it receive messages from the server. The amount of guesswork needed was actually the trigger for me to write this post, because I want to share this with the other TDD aficionados out there. </span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">So, here's the piece of code required to connect to the node.js server running Socket.IO. (see <a href="https://github.com/reaktor/competition/blob/master/test/websocket-client.js">websocket-client.js</a>)</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var WebSocketImpl = require('websocket-client').WebSocket</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> WebSocketClient = function(port) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return new WebSocketImpl(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'ws://localhost:' + port + '/socket.io/websocket')</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">To receive messages, you need to assign an <i>onmessage</i> handler as in (see <a href="https://github.com/reaktor/competition/blob/master/test/message-queue.js">message-queue.js</a>)</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function handler(message) { console.log(message) }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var ws = WebSocketClient(port);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> ws.onmessage = function(message) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> /*</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> * The messages are prefixed with a header that contains </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> * the size of the actual payload. </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Since we just want the </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> * JSON from the message, it's easy to find the </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">first </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> * curly brace and go from there.</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> */</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var data = message.data;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var jsonStart = data.indexOf('{');</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if(jsonStart >= 0) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var jsonString = data.substring(jsonStart)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var jsonObject = JSON.parse(jsonString)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> handler(jsonObject)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> };</span><br />
<div style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</div><div style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Note that actual message handling is delegated to a function named <i>handler</i> in the above example.</div><br />
</div><div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;"></span><br />
<div><span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; font-size: large;">Asynchronous Testing with Vows</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">This is not going to be an introduction to Vows, as you'll find such at the <a href="http://vowsjs.org/">Vows site</a>. This is more like a case study and a presentation of some modest tools we created.</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">The main test suite, in the file <i><a href="https://github.com/reaktor/competition/blob/master/test/competition-round-test.js">competition-round-test.js</a></i>, sets up a server with 2 challenges that will be sent to 4 contestant servers. Two of the contestants are set up to return a simple answer with different strategies each. One contestant is set up to be a bit too slow so that the server should time-out before getting the answer from this one. The fourth contestant always sends an invalid answer. A fake visualization client is connected to the server, and it will be used to check that the server sends correct messages at each stage of the competition round.</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">When all this is set up, the server is started and it's output to both the contenders and the visualization client is monitored asynchronously using Vows. The very first test is like this:</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var initialization = {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "When visualization client connects" :</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> expectMessage("initialization message is sent", {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> message : "init",</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> contenders : [{name : "TestContender8200"}, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {name : "TestContender8201"}, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {name : "TestContender8202"}, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {name : "TestContender8203"},</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {name : "TestContender8204", </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> rabbit : true}],</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> challenges : [</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {name : "Eka", numberOfItems : 2, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> capacity : [99], timeout : 50}, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {name : "Toka", numberOfItems : 2, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> capacity: [991], timeout : 150}]})</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">};</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;">Pretty clear? Should be. The expectMessage function there returns a Vows asynchronous <i>test context</i> that waits for a message to the fake visualization client and asserts that it is equal to the given message. In this case, it expects an initialization message that describes the contenders and the challenges. Here's the implementation:</span><br />
<span class="Apple-style-span" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function expectMessage(description, expectedMessage) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> topic: function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.waitForMessage(this.callback)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> description: function(message, _) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assert.deepEqual(message, expectedMessage);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> };</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">It's a Vows convention that there's a <i>topic</i> function in each test context. This function is called by the framework. In <i>expectMessage</i> the topic registers a message listener to the <i>messageQueue.</i> It actually hands over the vows callback function (this.callback) that the messageQueue will call when a message is received. Vows will then run each test case and pass the arguments of the callback to the test cases. In this example, the <i>description</i> test case will receive the <i>message</i> from messageQueue and makes an assertion on the message. The extra underscore (_) parameter in the description function was added because Vows seems to require at least two parameters. I guess this is a bug in the Vows version we used.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Are you wondering what the <i>message</i>Queue is? Well, that's a simple message queue of ours that's plugged into the fake visualization client. The source code for the message queue, as well as the rest of the fake visualization client can be found in <a href="https://github.com/reaktor/competition/blob/master/test/message-queue.js">message-queue.js</a>.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The rest of the competion round test is pretty much the same as the first case. One more tool is introduced though, for situations where we are expecting, say, 5 messages, the ordering of which is not important. For that, we created MessageBatcher (also in <a href="https://github.com/reaktor/competition/blob/master/test/message-queue.js">message-queue.js</a>) where the method <i>waitForMessages</i> calls the given callback when a given number of messages has been received. Using, this the results of the first challenge are tested like this:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var firstChallengeResults = {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "When contenders finish first challenge" : {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> topic : function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageBatcher.waitForMessages(5, this.callback);</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'timeout was 50ms': function(_, _) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assert.equal(round.challenges[0].timeout, 50)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> '100ms contender fails': function(messages, _) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assertContains(messages, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {message: "contenderFail", challengeName: "Eka", </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> contenderName : "TestContender8200"});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'client with ok result succeeds': function(messages, _) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assertContains(messages, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {message: "contenderReady", challengeName: "Eka", </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> contenderName : "TestContender8201", value: 100, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> weight : [10]});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'client with overweight fails': function(messages, _) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assertContains(messages, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {message : "contenderFail", </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">challengeName: "Eka", </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> contenderName : "TestContender8202"});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> 'invalid result fails': function(messages, _) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assertContains(messages, </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> {message : "contenderFail", challengeName : "Eka", </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> contenderName : "TestContender8203"});</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}; </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Lastly, we also want to verify that the contestants are presented with correct challenges. That's pretty easy, as we have our fake contenders capturing all input. So, in the last test batch, we'll do it like this:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var challengesSentToContenders = {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "For first challenge" : {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "correct challenge is sent" : function() {</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assert.deepEqual(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> client1.challenges[0], </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">round.challenges[0])</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "For second challenge" : {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> "correct challenge is sent" : function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> assert.deepEqual(</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> client1.challenges[1], round.challenges[1])</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}; </span><br />
<br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif; font-size: large;">So, did it work?</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif; font-size: large;"></span><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The competion was a success. I actually also participated in the actual competition too, as a part of a great Haskell team and it was fun. The competion server and the visualization there worked pretty nice, except for some occasional hangs caused by contestants returning surprising answers like Haskell error messages instead of JSON :) So, maybe the competition server should have been tested more with different kinds of invalid answers..</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><span class="Apple-style-span" style="font-size: large;">Problems..</span></span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Unfortunately, the guys who ran the competion were unable to get the Vows tests running on their machines. So there might be some mismatch with the most recent node.js and Vows versions.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">I'm running node 0.2.5 and vows 0.5.8 without problems. So, please clone <a href="https://github.com/reaktor/competition">competition-server</a>, install as in <a href="https://github.com/reaktor/competition/blob/master/README">README</a>, then run the tests with <i>run-tests.sh, </i>and tell me if it worked ok.</span></div></div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com2tag:blogger.com,1999:blog-1799557918826925298.post-29442475334891295482011-02-21T12:54:00.000-08:002011-09-07T10:55:14.875-07:00Game Sounds Using HTML5 and RX-JS<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">With HTML5 and Javascript, it's pretty easy to play sounds in the browser. And when you're using Reactive Extensions for Javascript, it's also pretty easy to "bind" sound effects to events. I created a simple "sound engine" to my Worzone game experiment and used RX to bind sounds to events. The source code is of course available in GitHub at <a href="https://github.com/raimohanska/worzone">https://github.com/raimohanska/worzone</a> and you can have a quick demo at <a href="http://juhajasatu.com/worzone/">http://juhajasatu.com/worzone/</a>. Just click on the "sound" checkbox before pressing any key to hear the stuff you're looking for.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Anyway, I'll briefly show you the code here too. Let's start with the "sound engine", which looks like this:</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Audio() { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var on = false</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var sounds = {}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function loadSound(soundName) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var audioElement = document.createElement('audio')</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> audioElement</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .setAttribute('src', "audio/" + soundName + ".ogg")</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return audioElement</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function getSound(soundName) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (!sounds[soundName]) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> sounds[soundName] = loadSound(soundName)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return sounds[soundName] <span class="Apple-tab-span" style="white-space: pre;"> </span> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function play(soundName) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (on) getSound(soundName).play()</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // String -> (Unit -> Unit)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> playSound : function(soundName) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return function() { play(soundName) }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> },</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> // Unit -> Unit</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> toggle : function() { on = !on; }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The Audio object exposes two methods, one of which is used to toggle sound on/off and the other returning a function that will play a given sound when invoked. The comments above the methods use the Haskell syntax for describing what are the input and output types of the functions. </span></span><br />
<br />
<br />
<br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"></span><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">One funny thing about HTML5 audio is that .ogg format is supported while .mp3 is not. Some patent issues there, I guess. The reason why playSound returns a function instead of playing a sound is that this way it's easier to plug it into RX streams, as in the following piece of code which constitutes the whole of game sound related code in Worzone:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function GameSounds(messageQueue, audio) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function sequence(delay, count) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return ticker(delay)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Scan(1, function(counter) {return counter % count + 1} ) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> sequence(500, 3)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .SkipUntil(messageQueue.ofType("level-started"))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .TakeUntil(messageQueue.ofType("level-finished"))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Repeat()</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Subscribe( </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function(counter) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> audio.playSound("move" + counter)() </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.ofType("start")</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Where(function (start) { return start.object.player })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Select(function(start) { return start.object.player.id })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Subscribe(function(id) { audio.playSound("join" + id)() }) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.ofType("fire")</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Subscribe(audio.playSound("fire"))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.ofType("hit")</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Subscribe(audio.playSound("explosion"))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.ofType("level-starting")</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Subscribe(audio.playSound("intro1"))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">} </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The messageQueue thing there is an RX Observable that I described in one of my previous postings. It will provide all game events, each containing a <i>message </i>property that can be used for filtering events. The <i>ofType</i> method actually provides an Observable containing only messages of the given type.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The actual sound effects are</span><br />
<br />
<ul>
<li><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">An alternating sequence of <i>move[1..3]</i> sounds played each 500 milliseconds, starting when a game level is started and ending when a level ends. So, no creepy beeping when the monsters aren't moving.</span></li>
<li><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Two sounds <i>join[1..2] </i>that are when player 1 or 2 starts the game.</span></li>
<li><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><i>Fire, explosion </i>and<i> intro</i> sounds that are played when somebody fires or dies and when a new game level is about to start.</span></li>
</ul>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">There seems to be some problem with my formatting. Extra line breaks appear. Not in the editor though. What's up? </span></div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com2tag:blogger.com,1999:blog-1799557918826925298.post-45698928470824105102011-02-18T12:04:00.000-08:002014-06-23T14:22:14.140-07:00Failing with RX-JS<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The first-ever example I've seen of Reactive Extensions for Javascript is the example, where you've got these +/- buttons that you can use to increment/decrement a counter:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS-7rNZ49532HzWwL1-gA_7VCoY7RigI2P8jMjJrmMgijrfjhbXUqXzLo9dv4CDDqd9mZDzHBUKfoPXASA9IuWMmi_aqZzOrpoNPZCgPTDmmA0T5pdOAQ5-kCq6SNsVEpuufGAFq5qzmw/s1600/Screen+shot+2011-02-18+at+21.37.44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS-7rNZ49532HzWwL1-gA_7VCoY7RigI2P8jMjJrmMgijrfjhbXUqXzLo9dv4CDDqd9mZDzHBUKfoPXASA9IuWMmi_aqZzOrpoNPZCgPTDmmA0T5pdOAQ5-kCq6SNsVEpuufGAFq5qzmw/s1600/Screen+shot+2011-02-18+at+21.37.44.png" /></a></div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">The code is nice and elegant as in</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;">$(function() {</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;"> function always(x) { return function(_) { return x }}</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;"> var incr = $('#incr').toObservable('click').Select(always(1))</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;"> var decr = $('#decr').toObservable('click').Select(always(-1))</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;"> var series = incr.Merge(decr)</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;"> .Scan(0, function(total, x) { return total + x })</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;"> series.Subscribe(function (total) { $('#count').html(total) })</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="white-space: pre;">}) </span></span><br />
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div>
<br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Great, isn't it? Well, I wasn't quite impressed. This is a toy example and doesn't really show the real power of RX. However, what do you think will happen with the following addition:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> $('#moar').click(function(){ </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> series.Subscribe( function(total) {</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">$('#count2').html(total)})</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
I'd expect that after clicking on the "moar" button, a duplicate counter would appear, showing the same number as the first one. However, it'll look like this:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7lei5xF5I9jnKO81yc44eob5ppOeaYjOvMXZ4NVBn33qMcSVhZhct5QZXBqj0c96OVAoeLC5ADA4nb0dYs1Vt5YbL8NC2emjzapty7cf4qgSKEhiUtpedxbm5pp8AY2xfUncmpYYbD5g/s1600/Screen+shot+2011-02-18+at+21.43.02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7lei5xF5I9jnKO81yc44eob5ppOeaYjOvMXZ4NVBn33qMcSVhZhct5QZXBqj0c96OVAoeLC5ADA4nb0dYs1Vt5YbL8NC2emjzapty7cf4qgSKEhiUtpedxbm5pp8AY2xfUncmpYYbD5g/s1600/Screen+shot+2011-02-18+at+21.43.02.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">How come? It seems that when a new Subscriber is added, it will get a new copy of the Observable series, starting from zero. </span><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">It seems that the accumulator in the Scan combinator is not shared by subscribers. I guess it's logical but it certainly took my by surprise. It doesn't feel right that different observers of the same Observable see different values. What do you think? </span></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">This can be fixed quite easily though:</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var series = incr.Merge(decr)</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Scan(0, function(total, x) { return total + x })</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Publish()</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> series.Connect()</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Not hard per se, but the real problem with RX is that you have to know all these tricks and there's no-one there to teach you (except some brave bloggers). Where is the public API documentation? Well, it's there for sure. Provided in Windows Help File format, in the file RxJS.chm that's bundled with the distro. Who uses .chm files for documentation?</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
According to Microsoft's fine documentation, the Publish() method "Returns a connectable observable sequence that shares a single subscription to the underlying source". It doesn't say that you have to call Connect() too to make it actually work. But hey, it doesn't say much else either. </div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
I HATE the way this library is documented. It makes me feel the authors are either cocky or stupid. The latter seems impossible regarding the quality of the actual code.</div>
<br />
<div style="font-family: Arial, Helvetica, sans-serif;">
EDIT: Sorry for the last line. I've got huge respect for the Rx team and by now they've improved documentation a lot too, but that was the situation and those were my feelings at the time I wrote this post.</div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com8tag:blogger.com,1999:blog-1799557918826925298.post-60361425176639632142011-02-15T08:23:00.000-08:002011-09-07T10:56:07.654-07:00Creating Custom Observables and Combinators in RX JS<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">I'm writing this because I feel there's not much stuff available on writing custom Observables in Reactive Extensions for Javascript.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif; font-size: large;">1. The Basics</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">First, I had to grasp how the subscribe/unsubscribe mechanism works. And it goes like this:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">1. You've got an observable</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var observable</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">2. You subscribe, and you get a handle for unsubscribing</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var disposable = observable.Subscribe(function(message){...})</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">3. You unsubscribe using the Dispose() method</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> disposable.Dispose()</span><br />
<br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">If you're not working on custom Observables or Combinators, you'll hardly ever need the Dispose() function, as you'll more likely use methods like Take, TakeUntil to select how many calls you really want your Subscriber to get.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">When creating my custom Observables, I have to make sure I return a working "dispose" function from the Subscribe function. For example, for a simple "ticker", it could work like this:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function ticker(interval) {</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return Rx.Observable.Create(function(observer) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>var id = setInterval(observer.OnNext, interval) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>return function() { clearInterval(id) }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">} </span><br />
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
So for a custom Observable, you need to provide a Subscribe function that's called whenever a new Subscriber is added. This function returns another function, that's used by RX when Subscriber is removed.</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;">2. The SampledBy combinator</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;"><br />
</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
In my Worzone project, I had the need to sample an Observable each 50 milliseconds. Observable.Sample() doesn't seem to work for me, because it only takes sample if there's a new event in the source stream. In my case, the source stream (a stream containing the current direction where a figure should go) gets events only when something changes. I still want to get a sample (the latest value) every 50 ms. Hence, a custom combinator!</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
First, I created a more general-purpose combinator called <i>CombineWithLatestOf</i>, that combines a stream with the latest value of another stream, using a given combinator function:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Rx.Observable.prototype.CombineWithLatestOf = function(otherStream, combinator) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var mainStream = this</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return Rx.Observable.Create(function(subscriber) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var latest</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var d1 = mainStream.Subscribe(function(mainValue) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> subscriber.OnNext(combinator(mainValue, latest)) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var d2 = otherStream.Subscribe(</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function(message) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> latest = message</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return function() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> d1.Dispose()</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> d2.Dispose()</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">} </span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Here you see the Dispose() method in action. When a Subscriber is added to the result stream, new Subscribers are added to both the source stream and the other stream (where we want the latest value from). When the Subscriber is removed, the Dispose() method is called for both "proxy" subscriptions. I challenge you to tell me what's missing from above, or how the same could be done in a more elegant fashion!</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Anyways, this combinator makes it easy to implement SampledBy:</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Rx.Observable.prototype.SampledBy = function(otherStream) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return otherStream.CombineWithLatestOf(this, function(a,b) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return b</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
<div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;">3. The MessageQueue Observable</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;"><br />
</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
The other major thing I couldn't find in RX-JS is a "message queue", where I can push messages and that implements Observable, delivering all the pushed messages to Subscribers. To go a bit deeper, I implemented a <i>plug</i> method that allows me to plug in other streams to the MessageQueue. The result is that I can plug all streams into this central bus that I can then filter to observe certain types of messages.</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
And here it is.</div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function MessageQueue() { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function remove(xs, x) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> xs.splice(xs.indexOf(x), 1)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function Subscription(observable) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var disposable </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function cancel() { remove(subscriptions, subscription)} </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function push(message) { messageQueue.push(message) }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function start() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> disposable = observable.Subscribe( push, cancel) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> function stop() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (disposable) disposable.Dispose() </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var subscription = {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> start : start, stop : stop</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> subscriptions.push(subscription)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (observers.length > 0) { start() }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return subscription;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var subscriptions = []</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var observers = [] </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var messageQueue = Rx.Observable.Create(function(observer) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> observers.push(observer)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (observers.length == 1) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> subscriptions.forEach(function(subscription) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> subscription.start() </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return function() { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> remove(observers, observer); </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (observers.length == 0) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> subscriptions.forEach(function(subscription) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> subscription.stop() </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.push = function (message) { </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> observers.map(identity).forEach(function(observer) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> observer.OnNext(message)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> });</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return messageQueue</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> messageQueue.plug = function (observable) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Subscription(observable)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return messageQueue</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return messageQueue</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">} </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Whoa. Sorry to spit out such a large chunk of code here. At first it was very simple, but then I discovered that I had been cheating in bookkeeping, I mean, not cleaning up all observers properly. That's quite a lot of bookkeeping there. I wish I could cut it down somehow. Can you?</span></div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com2tag:blogger.com,1999:blog-1799557918826925298.post-65035482253898274252011-02-12T00:10:00.000-08:002011-09-07T10:56:42.426-07:00Game Programming With RX-JS<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">I'm quite excited by Microsoft's RX (Reactive Extensions) for JavaScript. I 'm also nostalgic for old school Commodore 64 games such as Bruce Lee, Archon and especially Wizard of Wor. Would'nt it be be cool to be able to play WoW(!) online.. So, why not give it a try and start writing it in RX-JS. </span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">First, I want to be able to control the Pixel Man (his real name remains unknown to me):</span><br />
<br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"></span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhouMb2ybchfwqAfTnUuElKl31OqW4Ki3oKBuczWtn0FSf0a6NcLc2xctUtfOZ3DTJTc2HTelmRlcJ8Jn7_A_iGpKWXicywmw5nnUssuWP7Lsnx6dr24Gp-iWcVAGFi_fqEAsRWbXfwY-A/s1600/man2-large.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhouMb2ybchfwqAfTnUuElKl31OqW4Ki3oKBuczWtn0FSf0a6NcLc2xctUtfOZ3DTJTc2HTelmRlcJ8Jn7_A_iGpKWXicywmw5nnUssuWP7Lsnx6dr24Gp-iWcVAGFi_fqEAsRWbXfwY-A/s1600/man2-large.png" /></a><br />
<br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif; font-size: large;">1. Keyboard Control</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">So, I started by defining some streams. This is actually the hardest part, so bear with me for a while.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<br />
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>function keyState(keyCode, value)</b> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return Rx.Observable.FromArray([[]])</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Merge(keyDowns(keyCode).Select(always([value]))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .Merge(keyUps(keyCode).Select(always([])))</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .DistinctUntilChanged())</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div>
</div>
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var allKeyUps = $(document).toObservable("keyup")</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var allKeyDowns = $(document).toObservable("keydown")</span><br />
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function always(value) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return function(_) { return value } }</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function keyCodeIs(keyCode) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return function(event) { return event.keyCode == keyCode} }</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function keyUps(keyCode) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return allKeyUps.Where(keyCodeIs(keyCode)) }</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function keyDowns(keyCode) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return allKeyDowns.Where(keyCodeIs(keyCode)) }</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
.. and I've got a stream of a given key's state, mapped into a single-element array in case the key is down, or the empty array if the key is up. Like below..</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">allKeyDowns ...keydown......................</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> |</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">allKeyUps ...|.............keyup..........</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">| | </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">keyState(key, "LOL") ...["LOL"].......[].............</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br />
The keyState function starts with the empty array, and merges in all keyDowns as single-element arrays and all keyUps as empty arrays.</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Then, I combine a bunch of these streams so that I can get the state of multiple keys are a set of values:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>function multiKeyState</b>(keyMap) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var streams = keyMap.map(function(pair) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return keyState(pair[0], pair[1]) })</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return Rx.Observable.CombineLatestAsArray(streams)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
</div>
<div>
<div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
I created some custom RX combinators for this:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>Rx.Observable.CombineLatestAsArray = function(streams)</b> { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return Rx.Observable.CombineAll(streams, function(s1, s2) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return s1.CombineLatest(s2, concatArrays)}) </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><b>Rx.Observable.CombineAll = function(streams, combinator)</b> {</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var stream = streams[0]</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> for (var i = 1; i < streams.length; i++) {</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> stream = combinator(stream, streams[i])</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return stream;<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div>
</div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function toArray(x) { return !x ? [] : (_.isArray(x) ? x : [x])}</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function concatArrays(a1, a2) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return toArray(a1).concat(toArray(a2)) }</span></div>
</div>
<br />
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
</div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
This finally allows me to map the arrow keys into direction vectors that define where the Pixel Man shall go:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var keyMap = [</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> [38, Point(0, -1)],</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> [40, Point(0, 1)],</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> [37, Point(-1, 0)],</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> [39, Point(1, 0)]</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">]</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var direction = </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> multiKeyState(keyMap).Where(atMostOne).Select(first)</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
The <i>direction </i>stream will then map my keystrokes like below:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> keydown:left......keyup:left....keydown:right</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> | | |</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Point(-1, 0) undefined Point(1, 0)</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">.. the Point function naturally returning simple x/y pairs.</span></div>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span></div>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif; font-size: large;">2. Movement</span></div>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif; font-size: large;"><br />
</span></div>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">To make the man move, I will start with a <i>startPos,</i> sample the <i>direction</i> stream every, say 100 ms, and increment the position by the currect direction. Simple as that:</span></div>
<div>
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span></div>
<div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<div>
<div>
function identity(x) { return x }</div>
</div>
<div>
<b>var ticker = </b>Rx.Observable.Create(function(observer) { setInterval(observer.OnNext, 100) })</div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<b>var movements</b> = ticker.CombineLatest(direction, function(_, dir) { </div>
<div style="font-family: Arial, Helvetica, sans-serif;">
return dir }).Where(identity)</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<b>var position</b> = movements.Scan(startPos, function(pos, move) { </div>
<div style="font-family: Arial, Helvetica, sans-serif;">
return pos.add(move.times(4)) })</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
I started by constructing a <i>ticker</i> stream that will generate an event every 100 ms. The <i>movements</i> stream was created by mapping the ticker events into the current direction using CombineLatest, and filtered out the <i>undefined</i> values by using Where(identity). Finally, the position stream is a kind of a sum of all movements, with the starting value of <i>startPos.</i></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<i><br />
</i></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
At this point I might mention that the Point class that represents position and movement vectors (x/y pairs) has some methods<i> </i>for adding and multiplication, as can be seen in the defining function of the position stream. Please have a look at the full source code.</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;">3. Graphics</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;"><br />
</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
So far this has been functional programming, without any side effects or mutable state. As you can see, I'm tracking keyboard state and Pixel Man position without any explicit state variables, thanks to RX!</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
To make the Pixel Man materialize and start moving, we need to select some graphics framework and assign side-effects to the position stream. So, I'll start by initializing Raphael and adding the Pixel Man on a black background:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var bounds = Rectangle(0, 0, 640, 480)</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var r = Raphael(10, 10, bounds.width, bounds.height);</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">r.rect(bounds.x, bounds.y, bounds.width, bounds.height)</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .attr({fill : "#000"})</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var startPos = Point(100, 100)</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var man = r.image("man1.png", startPos.x, startPos.y, 40, 40)</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Now that I've got the man on the stage and I've got the streams set up, I can make him move with a nice one-liner:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">position.Subscribe(function (pos) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> man.attr({x : pos.x, y : pos.y}) })</span></div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;">4. Animation and Rotation</span></div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
It was a bit dull to see the Pixel Man float around, even though he was in my control, so I added animation:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var animation = movements.Scan(1, function(prev, _) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return prev % 2 + 1})</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">animation.Subscribe(function (index) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> man.attr({src : "man" + index + ".png"})}) </span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Now that was cool, wasn't it? No poking around the code, just one new stream and a side effect. The stream maps movements in to an alternating series of 1's and 2's. The side-effect alters the image between the two png's that I've got.</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Finally, I made the man look where he's going, instead of just looking right:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var angle = direction.Where(identity).Select(function(vec) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return vec.getAngle()})</span></div>
<div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">angle.Subscribe(function(angle) { </span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> man.rotate(angle * 360 / (2 * Math.PI) + 180, true) })</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
</div>
</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Same thing here: a stream of angles and a side effect that rotates the Pixel Man.</div>
<div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;">5. Some fixing</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;"><br />
</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
I was happy with the simplicity and elegance of how I implemented animation and rotation. However, I wasn't so happy with the man turning upside-down when he was moving to the right.. So, I replaced the elegant code with something a little less elegant, but still quite simple:</div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var animAndDir = direction.Where(identity)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> .CombineLatest(animation, function(dir, anim) { </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return {anim : anim, dir : dir}})</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">animAndDir.Subscribe(function(state) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var angle, basename</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (state.dir == left) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> basename = "man-left-"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> angle = 0</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } else {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> basename = "man-right-"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> angle = state.dir.getAngle() * 360 / (2 * Math.PI)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> man.rotate(angle, true)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> man.attr({src : basename + (state.anim) + ".png"})</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">})</span><br />
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span></div>
</div>
<div>
<span class="Apple-style-span" style="font-size: large;">6. Conclusion</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<span class="Apple-style-span" style="font-size: large;"><br />
</span></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
Easy wasn't it? After some learning and having mastered the keyboard state, it was quite trivial to make the Pixel Man move, rotate and animate.<br />
<br />
Now I have a black screen with two keyboard-controlled Pixel Men. I've played it with my daughter and she coined it the Robot Game. At 1 year and 11 months it's freaking awesome to be able to control a robot.<br />
<br />
<1 week later><br />
<br />
After I wrote this, I hacked some more features into the game. Now there are two players and also some enemies there.<br />
<br />
The code is available at <a href="https://github.com/raimohanska/worzone">https://github.com/raimohanska/worzone</a><br />
Plx check out a live demo at <a href="http://juhajasatu.com/worzone/">http://juhajasatu.com/worzone/</a></div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br />
Worx great in Chrome and Safarei, but a bit slowly in Firefox. Just like with the Lavalamp I blogged about earlierly. </div>
<div style="font-family: Arial, Helvetica, sans-serif;">
<br /></div>
Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com3tag:blogger.com,1999:blog-1799557918826925298.post-86783781915329490892011-01-21T08:33:00.000-08:002011-01-21T08:34:13.824-08:00The retVal pattern<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Don't you just love the retVal pattern? I'm sure you've spotted methods like</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">f</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">unction getSalary() {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> var retVal;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> if (sex == 'Male') {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> retVal = 9500;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> } else if (sex == 'Female') {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> retVal = 500;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> return retVal;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">There's some retVal for you. Once you've spotted a retVal, you're likely to encounter some arrowheads too. </span><span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">If you are one the Knights of A Single Exit Point, you may even like it.</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;">Or you could just zip your dog with its own tail, as in</span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><br />
</span><br />
<div style="font-family: 'Courier New', Courier, monospace;">Prelude> let dog = "dog"</div><div style="font-family: 'Courier New', Courier, monospace;">Prelude> zip dog (tail dog)</div><div style="font-family: 'Courier New', Courier, monospace;">[('d','o'),('o','g')]</div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com1tag:blogger.com,1999:blog-1799557918826925298.post-32301617899369039112011-01-17T05:33:00.000-08:002011-01-17T05:49:55.640-08:00Particle Simulation With HTML Canvas<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDpa_fKEb4_uSTD7nxtruANr-vA-eDXNGn_mrMfgA0pLwsNP0pbgPeg54fY2t53zUqu6CIXcK79aEZhirB9jnfASbCJBN2OQyj9ZvWknOZiHQ0d9NE5LPAeRXFb6t_ERpUOscPB2NahYo/s1600/javascript-lava-lamp.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br />
</a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDpa_fKEb4_uSTD7nxtruANr-vA-eDXNGn_mrMfgA0pLwsNP0pbgPeg54fY2t53zUqu6CIXcK79aEZhirB9jnfASbCJBN2OQyj9ZvWknOZiHQ0d9NE5LPAeRXFb6t_ERpUOscPB2NahYo/s1600/javascript-lava-lamp.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br />
</a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDpa_fKEb4_uSTD7nxtruANr-vA-eDXNGn_mrMfgA0pLwsNP0pbgPeg54fY2t53zUqu6CIXcK79aEZhirB9jnfASbCJBN2OQyj9ZvWknOZiHQ0d9NE5LPAeRXFb6t_ERpUOscPB2NahYo/s1600/javascript-lava-lamp.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDpa_fKEb4_uSTD7nxtruANr-vA-eDXNGn_mrMfgA0pLwsNP0pbgPeg54fY2t53zUqu6CIXcK79aEZhirB9jnfASbCJBN2OQyj9ZvWknOZiHQ0d9NE5LPAeRXFb6t_ERpUOscPB2NahYo/s1600/javascript-lava-lamp.png" /></a></div>My wife was watching Desperate Housewives on the couch. I felt comfortable sitting there except that my eyes were burning. Hence, I chose to code a virtual lavalamp in Javascript, using HTML5 canvas.<br />
<br />
I noticed that the graphics part was quite easy, except that I had to google how to draw a circle. This is how:<br />
<br />
ctx.arc(x, y,radius,0,Math.PI*2,true);<br />
<br />
I also discovered that my lava lamp is quite fluid in Chrome but incredibly slow in Firefox. Why so? Plz tell me.<br />
<br />
Have a look at the code at https://github.com/raimohanska/particles.<br />
<br />
Live demo @ <a href="http://juhajasatu.com/particles/">http://juhajasatu.com/particles/</a><br />
<br />
I could write a longer story on this, but I don't have time right now. Maybe the code will speak for itself, though. I'm pretty happy with it, having separated concerns like particle interaction, rendering, and particle heating by the "lamp" on the bottom. It's fun to tweak the parameters every now and then.Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com1tag:blogger.com,1999:blog-1799557918826925298.post-87295905403287981922010-11-16T13:39:00.000-08:002010-11-16T13:39:40.225-08:00Some Classes in JavaScriptI did some client-side coding using MooTools. There, as a long-time Java developer, I liked the way I could define classes like in some more object oriented languages:<br />
<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">var Vehicle = new Class({</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">drive : function() {</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">console.log("I'm driving a " + this + " with a top speed of " + this.topSpeed);</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">},</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">});</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">var Car = new Class({</span></span><span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">Extends : Vehicle,</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">topSpeed : 250,</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">initialize : function(make, model) {</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">this.make = make;</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">this.model = model;</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">},</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">toString : function() {</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> return this.make + " " + this.model;</span></span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"> </span></span></span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">},</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">});</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;">new Car("Fjord", "Taunus").drive();</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"><br />
</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: Times;">Then I went on to server-side JavaScript, namely on node.js. There I immediately felt a bit weak without the Class() constructor. I did some googling to see if I could have Mootools on server-side too, but got under the impression that it's not that easy, requiring some customization of Mootools. Hence, I decided to roll my own. And here it is:</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"><span class="Apple-style-span" style="font-family: Times; font-size: medium;"><br />
</span></span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-size: small;"></span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">function Class(props) {<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>function copyProperties(from, to) {<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>for (var key in from) {<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>to[key] = from[key];<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>}<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>var constructor = function() {<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>props.initialize.apply(this, arguments);<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>};<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>if (props.Extends) {<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>copyProperties(props.Extends.prototype, constructor.prototype);<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>copyProperties(props, constructor.prototype);<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>return constructor;<br />
}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: Times;"><br />
</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: Times;">That's not so much code after all, is it? The code in the first snippet actually works with my Class constructor. As you can see, it supports subclassing using 'Extends', a constructor named 'initialize' and any number of methods and properties.</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="font-family: Times;"><br />
</span></span><br />
It took me a couple of hours to get these roughly 10 lines of code together though. I had to realise that<br />
<br />
<br />
<ol><li>The "class" is actually a constructor function. Hence, the Class() function has to return a constructor</li>
<li>In any Javascript function, you have an "arguments" array (well not stricly an array but..) that you can use to access all arguments</li>
<li>You can call any Function using its <i>apply </i>function with the "this" object and a list of arguments</li>
<li>You can easily copy properties of an object by looping them using "for key in object"</li>
</ol><div>I have to admit that I'm still a bit confused how this all works though :)</div><div><br />
</div><div>Feel free to prove my über-simple class system wrong and point me back to the correct path.</div>Juha Paananenhttp://www.blogger.com/profile/11863701709479259865noreply@blogger.com1