donderdag 17 januari 2013

Promises, Promises!

I’m developing backbone front-end apps for almost a year now. Lately the role of asynchronous objects is increasing in the projects I work on.

Imagine the following code (in coffeescript):

class Products extends Backbone.Collection  findByArticleCode: (code) -> # return a product we know p = @find (product) -> product.get('code') is code return p if p?  # try to find product through back-end p = new Product { code } @add p p.fetch() p

This method returns an Object either populated or one in a state of flux (you never know if the fetch will be successful). To make the behavior a bit more consistent, I started to use the triggers mechanic more. I fired a trigger when I found the cached object or when the back-end request returned successfully.

The code as a consumer the started to look like this:

products = new Products products.on 'fetch:product', (product) -> # do something with the product products.findByArticleCode('abcd')

This way it works in all cases, but there are some major problems:

  1. Readability. The code above is backwards. First the handling is described, before an article is fetched anyway
  2. Magic. The ‘fetch:product’ trigger seems to come out of nowhere, and can be triggered by other findBy… methods
  3. Error handling. I now need to trigger some ‘product not found’ event to do exception handling

It gets worse if you need to wrap this fetched class into another and do a request for that object as well and wait for that request to be succesful as well.

A colleague pointed me to this video: I .promise() to show you .when() to use Deferreds by Alex McPherson

Deferreds are exactly what I was looking for!

the code started to change:

findByArticleCode: (code) -> def = $.Deferred() p = @find (product) -> product.get('code') is code if p? def.resolve(p) else p = new Product { code } p.fetch() .done((product) => @add product) .done((product) -> def.resolve product) def

This way this method always returns the same. A so called ‘promise’. This code may not seem that different, but using the code is changed dramatically:

products = new Products products.findByArticleCode(‘abcd’).then(product) –>

# do something with the product

Advantages of this code:

  1. No ‘trigger’ magic between them
  2. No registering first and calling later
  3. Less administration by unregistering
  4. Then is a direct cause of the call, others can not intervene here (like others using same trigger)

To test code like this, I wanted to use Chai-as-promised but stubled across the following issue: Chai as promised is incompatible with jQuery so-called ‘promises’

Dominic Denicola pointed me to this great write-up about promises: You’re Missing the Point of Promises

So what are promises?

The article above does a far better job explaining than I do, but for sake of completeness I want to give a short explanation. For the details, please read You’re Missing the Point of Promises

The promise pattern/mechanic is basically a way to mimic a synchronous function. A synchonous function is basically something that accepts arguments, and returns a value/object. When something goes wrong in the returning of this function an exception is thrown.

synchronousMethod: (arg) -> if arg > 10 throw new Error "Arg can't be larger than 10!" else return arg + 10

In asynchronous code, when the method is executed, we don’t know its return value yet. And we also don’t know already if the method will eventually fail.

In the past this problem was solved by providing callbacks. Mostly a callback if stuff went well, and one when stuff went wrong.

Promises are a way to streamline this behaviour.

You basically call a method (for example: fetchObjectFromServer) and you get a promise back. This promise claims to have eventually fetched an object from a remote server. So a promise also has a return value (namely the fetched object). A callback can be bound to this promise to retrieve this return value when it is available. To handle errors, a callback can also be attached to do the error handling.

It seemed that the implementation of promises done by the jQuery guys is actually a faulty one and not compatible with the ‘specs’ of Promises/A

There is a lot more to promises then meets the eye. Mainly in the case that each part of the callback chain is also a promise. Again, the gist post is better in explaining than I am :–)

The Promises/A page also has a lists of valid implementations, and I switched to ‘when’ (tried ‘Q’) but ‘when’ seems to have faster performance (test times where lower).

The changed code now looks like this:

findByArticleCode: (code) -> def = window.when.defer() p = @find (product) -> product.get('code') is code if p? def.resolve(p) else p = new Product { code } p.fetch() .done (product) => @add product) def.resolve product def.promise()

So only the second line has changed. But now you can as a consumer of the promise do better chaining and error handling.

Testing promises

The test code for this now also looks awesome: (mocha / chai)

beforeEach -> @product = new Product code: 'abcd' @products = new Products [@product]  describe '.findByArticleCode', -> it 'fetches already known products', (done) -> @products.findByArticleCode('abcd').should.become(@product).and.notify(done)

The .notify(done) part is needed for the asynchronity of the test. Hopefully I will be ably to plug mocha-as-promised into Konacha soon so that it is no longer necessary anymore.

Since I also like to fire triggers and test if that is happening tests get a bit mode cumbersome again:

it 'lists the new page as active when fetched', (done) -> spy = sinon.spy() @pages.on 'change:active', spy  @router.showPage('other/page').then => spy.should.have.been.calledWith(@nextPage, yes) done() , (error) -> done new Error error

Synchronous trigger testing can be done using chai-backbone plugin I wrote some time ago.

@object.should.trigger('eventname').when -> @object.trigger('eventname')

But this didn’t work for promises yet.

I have updated the chai-changes plugin that promises are supported as return value of the method provided in when.

The ‘when’ block can return a promise now, and the post-conditions are run when the promise is fulfilled. The promise returned is a promise about the expectations, and not the promise returned by the callback. So if the callback returns a promise that is resolved, but the expectations after that resolved promise fail, the ‘when’ method returns a rejected promise with the assertion error as argument.

When using the ‘mocha-as-promised’ extension, the code can then be cleaned up to:

it 'lists the new page as active when fetched', -> @pages.should.trigger('change:active', with: [@nextPage, yes]).when => @router.showPage('other/page')

Which is considerable less code then above but also more readable.

I use Konacha to test my code, and it runs the test in a browser (or headless browser using the konacha:run rake task). Unfortunately, I am not yet able to get the mocha-as-promised working with Konacha, so I needed to fall back to using the ‘notify’ chaining method:

it 'lists the new page as active when fetched', (done) -> @pages.should.trigger('change:active', with: [@nextPage, yes]).when(=> @router.showPage('other/page')).notify(done)

Promises are really cool to use to handle the asynchronous aspects of your front-end logic, and using chai, chai-as-promised and now also the chai-changes it is really good to test these kind of processes! If you are using Konacha to test your front-end code, be sure to checkout out Konacha-chai-matchers gem

Useful sources

Posted via email from posterous of Matthijs Groen

donderdag 13 december 2012

Pair programming using TMux

At Kabisa we are big fans of pair programming. Unfortunately, we can’t always sit fysically together when doing so. In this case, we do remote pair programming using TMux and SSH.

To attach to the same session, the host connects to his own machine using SSH, and the client does as well. We use public keys for this purpose. But since I don’t want anyone in my own user account directly, I create a user with standard priviledges, called ‘pairing’.

To make a long story a bit shorter:

Host:

  • ssh pairing@localhost
  • start TMux, that sudo’s to account of host, kicks of another TMux as host
  • bootstrap dev environment

Client:

  • ssh paigin@pairinghost
  • tmux attach

Here are the instructions to build such an environment:

Setup remote pair programming

This guide will help you setup a pair-programming environment using Tmux, and ssh.

We are going to use a tmux session on your own account (since you already have your project environment there) So the setup will be:

  1. You and your pair will login to your own machine using SSH
  2. One of you will kick off a Tmux session that is totally stripped of visual elements that, in turn will kick-off a tmux session as the user with the project environment. The reason to do this is if the tmux is strictly running as the paired user, you need to switch user for every window or pane created.
  3. In the nested tmux session, the environment will be bootstrapped using teamocil. Teamocil is used to setup windows, session name and kick of commands for various panes.
  4. The teamocil config file will be made dynamic so that its setup file can be checked in into the project repository so that every team member can use the same environment setup.

Initial setup

  1. install tmux: brew install tmux
  2. setup a nice tmux config: eg. TMux config of Matthijs Groen (this config assumes the use of a Powerline patched font)
  3. setup a user account, eg. ‘Pairing’. Give this user standard permissions.
  4. setup remote login for this user: System preferences -> Sharing -> Remote login
  5. setup public key login for this user on your own machine, so that you can use ssh pairing@localhost without using a password.
    1. sudo cp ~/Downloads/id_dsa.pub /Users/pairing/
    2. su pairing
    3. cd ~
    4. mkdir .ssh
    5. chmod 700 .ssh
    6. cat id_dsa.pub >> .ssh/authorized_keys
    7. rm id_dsa.pub

Project setup

  1. Create a ./start-.sh file in the home folder of the ‘pairing’ user:

    #!/bin/bash tmux -f .tmux-bare.conf new "sudo -u  -i tmux"

    the .tmux-bare.conf has the following contents (also in home of ‘pairing’):

    ## keybindings set -g remain-on-exit off set -g default-terminal "screen-256color" set -g status off
  2. chmod +x ./start-.sh

  3. setup a sudo rule that the pair user may run tmux as your own user

    sudo visudo

    setup in this file the rule that the user pairing may run tmux as another user:

    pairing  ALL=() NOPASSWD: /bin/zsh -c tmux
  4. setup in your own () alias file the following rules: (eg. .zshrc)

    # Projects export _ROOT='~/path/to/project' alias tmux-="tmux rename-window 'Zoom' && tmux set-option default-path ${_ROOT} && teamocil --layout ${_ROOT}/tools/teamocil.yml"
  5. install teamocil for your own user globally: sudo gem install teamocil

Using Tmux

Starting session

ssh pairing@localhost `./start-.sh`

in the first tab: tmux-

Joining session

ssh pairing@IP tmux attach

Leaving session

Ctrl+b d (detach-session) *

Killing session

Ctrl+a q (kill-session)

* Ctr+a is the TMux binding of your user TMux. The Pairing TMux will also automatically close when the session of the user TMux ends

* Ctr+b is the TMux binding of your pairing TMux.

Posted via email from posterous of Matthijs Groen

dinsdag 14 februari 2012

Backbone context manager

I'm trying to build a large scale backbone application. This requires the layout to have different contexts. I was looking at Backbone.layout_manager, but somehow couldn't grasp the magic that is performed there and if it was what I needed.

What I needed

I needed a simple switch state that my routers could use to have the correct view elements visible for the required scope.

See it like having 2 routers that require a 2 column markup, and one router requiring a one-column markup.

Since I'm building the site using Rails and therefor use the asset pipeline with coffeescript, this is the class I ended up with:

 

The code is best viewed with some example layout and context:

What happens is that the switching to the context removes classes on certain elements and add others. These classes should in your CSS show or hide the components. The reason everything is done with classes is simple. Transition effects.

.left-side-panel, .right-side-panel {
  -webkit-transition: all 200ms ease;
  opacity: 0;
  margin-top: 100px;
  &.active {
     margin-top: 0;
     opacity: 1;
  }
}        

you'll get the idea. This way you can really morph your one column layout to multiple columns etc.

Enjoy!

Posted via email from posterous of Matthijs Groen

woensdag 22 december 2010

Quick Ruby install

Install packages:
  sudo apt-get install build-essential ruby rubygems git curl
Install RVM:
  bash
The first timeyou install RVM, you must put the following line into
your ~/.bash_profile at the very end, after all path loads etc:
  [[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" #
This loads RVM into a shell session.

Setup RVM packages:
rvm package install readline
rvm package install zlib
rvm package install openssl
rvm install 1.8.7 --with-openssl-dir=$rvm_path/usr
--with-readline-dir=$rvm_path/usr --with-zlib-dir=$rvm_path/usr

Why RVM doesn't install the packages automatically is beyond me,
because I hoped RVM would make my life easier...

Posted via email from posterous of Matthijs Groen

zondag 24 oktober 2010

Postgres and PostGIS setup for Rails 3

In ubuntu, add as source: ppa:ubuntugis/ubuntugis-unstable In terminal:

~$ sudo apt-get install postgresql-8.4-postgis ~$ sudo su postgres postgres@your-desktop:/home/username$ createuser username (your linux username) Shall the new role be a superuser? (y/n) y postgres@your-desktop:/home/username$ exit

Now it is time to create the databases

make sure your database.yaml file looks like this:

development: adapter: postgresql database: project_development username: username

(etc for test and production)

In the Gemfile, you should have the following gems enabled:

gem “pg” gem “nofxx-georuby” gem “spatial_adapter”

after bundle install, you should be able to run rake db:create

Now we need to add the PostGIS stuff to the database

sudo su postgres createlang plpgsql project_development psql -d project_development -f /usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql psql -d project_development -f /usr/share/postgresql/8.4/contrib/postgis-1.5/spatial_ref_sys.sql psql -d project_development -f /usr/share/postgresql/8.4/contrib/postgis_comments.sql

And now we can use spatial columns in our database and migrations!

add_column :assets, :location, :point, :srid => -1 add_column :assets, :ground_space, :line_string, :srid => -1

add_index :assets, :location, :spatial => true add_index :assets, :ground_space, :spatial => true

Enjoy!

Posted via email from posterous of Matthijs Groen

Game weer tot leven wekken

Ik ben weer eens begonnen met de code van het spel tot leven aan het wekken. De code te porten naar Rails 3, en vooral de hele Postgres database opnieuw op te bouwen (om een of andere reden kan er niet meer geconnect worden)

Kortom flinke klussen, en als dat gebeurt is weer eens een status check doen hoe de broncode erbij ligt en dan kijken hoe we meer functionaliteit toe gaan voegen. Wordt nl tijd om weer eens richting een grafische client te gaan...

Posted via email from posterous of Matthijs Groen

donderdag 18 februari 2010

Weer een succesvolle proximity test

Ik had al tests gemaakt waarin een trigger actief werd als er iemand in de buurt kwam (de 3 vs 1 marine case). Echter was er nog geen mechanisme met berichtgeving wanneer iemand weer buiten bereik kwam. Hier is nu ook een mooi testje voor gemaakt. Ik heb een stationaire gun op het speelveld gezet, met een flinke actie radius.
Vervolgens laat ik 1 marinier vanaf grote afstand langslopen (maar wel de straal van de gun doorkruisen) en 1 marinier laat ik vlak langs de gun lopen. Degene die vlak langs loopt is het langst in bereik van de gun en zou dus de meeste schade op moeten lopen.

En dat was ook zo :-)
Degene op afstand liep 50% schade ongeveer op (had 73 van de 150 punten over)
en degene die dicht bij liep stierf.

Weer een test geslaagd!

Posted via email from posterous of Matthijs Groen