GitHub LinkedIn RSS
Sunday, July 27, 2014

JavaScript Builder Design Pattern


Today I would like to continue the series about design patterns we started with Factory Patterns in JavaScript and talk about the Builder pattern. For some reason it's being left behind in any design pattern usage in JavaScript. Maybe it was correct in the front end domain, but surely not in Node.js.

Builder pattern separates the construction of a complex object from its representation so that the same construction process can create different representations. The Builder pattern is based on Directors and Builders. Any number of Builder classes can conform to an IBuilder interface, and they can be called by a director to produce a product according to specification. The builders supply parts that the Product objects accumulate until the director is finished with the job. I'll be using the example from the factory pattern article to emphasize the differences between the patterns. Consider the class diagram for the Builder pattern:


And the implementation:
function FastCPU() {
    this.performOperation = function() {
        console.log("Operation will perform quickly");
    }
}
function SlowCPU() {
    this.performOperation = function() {
        console.log("Operation will perform slowly");
    }
}
 
function ExpensiveMotherBoard() {
    this.storeData = function() {
        console.log("There is a lot of RAM to store the data");
    }
}
 
function CheapMotherBoard() {
    this.storeData = function() {
        console.log("Little RAM. Swap file is used");
    }
}
 
function HighBudgetMachineBuilder() {
    this.getCPU = function() { return new FastCPU(); }
    this.getMotherBoard = function() {
     return new ExpensiveMotherBoard();
    }
}
 
function LowBudgetMachineBuilder() {
    this.getCPU = function() { return new SlowCPU(); }
    this.getMotherBoard = function() {
     return new CheapMotherBoard();
    }
}

function Director() {
    this.assembleMachine = function(builder) {
        var cpu = builder.getCPU(), 
        board = builder.getMotherBoard();
        return {
            cpu: cpu,
            board: board,
            test: function test() {
                this.cpu.performOperation();
                this.board.storeData();
            }
        }
    }
}
 
var director = new Director(),
    highBuilder = new HighBudgetMachineBuilder(),
    lowBuilder = new LowBudgetMachineBuilder(),
    highMachine = director.assembleMachine(highBuilder);
    lowMachine = director.assembleMachine(lowBuilder);
highMachine.test();
lowMachine.test();
We've added the director which assembles the machine. The client on the other hand, is not aware of the process of assembling the machines. It only uses the Director's method to do so.

The Builder and Abstract Factory patterns are similar in that they both look at construction at an abstract level. However, the Builder pattern is concerned with how a single object is made upby the different factories, whereas the Abstract Factory pattern is concerned with what products are made. The Builder pattern abstracts the algorithm for construction by including the concept of a director. The director is responsible for itemizing the steps and calls on builders to fulfill them. Directors do not have to conform to an interface.
Sunday, July 20, 2014

Jasmine and Node.js


Following our last article about using Jasmine and Karma together, let's look how we streamline the testing of our server side code written in Node.js. To do this, we'll need to install jasmine-node package.
npm install -g jasmine-node
If you're not familiar with the tool, please read the JavaScript Developer Toolkit before proceeding any further. The installed version will only support Jasmine 1.3 version. In order to add the 2.0 support, you should install the branched version.
npm install -g jasmine-node@2.0.0-beta4
Again as in previous article we'll be using the specs from our previous Jasmine article and the source code can be found in GitHub. After installing the correct version, we'll have to make our Player and Song classes as CommonJS modules. If you don't know how, please read the article about Modular Design Patterns in JavaScript. And of course requiring them in our specs:
...
module.exports = Song;
...
module.exports = Player;
describe("Player", function() {
  var Player = require('../src/Player.js');
  var Song = require('../src/Song.js');
  ...
Everything is ready - let's run the spec. We do this by pointing the path of the spec
jasmine-node spec/
Andddd bummer - we receive an error:
Exception loading helper: c:\GitHub\jsdeepdive-jasmine-and-nodejs\spec\SpecHelper.js
[ReferenceError: beforeEach is not defined]
This happens because customer helpers are not supported :(
Removed Support for Custom Helpers (have to be inside a beforeEach, this is a jasmine change, check out their docs on how to write one)
So let's put our helper inside the spec.
  beforeEach(function() {
    player = new Player();
    song = new Song();

    jasmine.addMatchers({
        toBePlaying: function () {
          return {
            compare: function (actual, expected) {
              var player = actual;

              return {
                pass: player.currentlyPlayingSong ===
                  expected && player.isPlaying
              }
            }
          };
        }
      });
  });
Trying again starts to passing through specs, but fails on the last one:
Failures:
  1) Player #resume should throw an exception if song is already playing
    Message:
      Expected function to throw an Error, but it threw Error: song is already
      playing.
    Stacktrace:
      Error: Expected function to throw an Error, but it threw Error: song is
      already playing.
    at Object. (c:\GitHub\jsdeepdive-jasmine-and-nodejs\spec\PlayerSpec.js:71:10)
Finished in 0.007 seconds
5 Tests, 1 Failures, 0 Skipped
And this is strange, cause we for sure know that example specs should all pass. This happens because of the bug/feature of jasmine-node (don't forget it's still a Beta) and we'll change our code a bit to bypass the problem by using toThrow instead of toThrowError:
expect(function() {
  player.resume();
}).toThrowError("song is already playing");
expect(function() {
  player.resume();
}).toThrow(new Error("song is already playing"));
Hooray - it works now and all the spec pass. Yes it's a bit quirky and yes more work is needed, but it's a progress. Next time I'll show how to use Grunt, which offers a better support for Jasmine 2.0.
Sunday, July 13, 2014

Jasmine and Karma


Let's talk about testing again, and will surely again in the future :) Last time I introduced the JavaScript Testing with Jasmine and we ran a few specs, however it seemed some how half baked - you were required to create a special page and open it in the browser to see the results. I you wanted to check in various browsers, you should have iterated the same procedure on each browser again and again.

This is not how we do things in 2014! We want to streamline the process! This is where Karma comes. It is built on Node.js and allows you to run the tests of your front end code automatically on various browsers.

Installation


In order to install Karma on your computer, run the following npm command. If you're not familiar with the tool, please read the JavaScript Developer Toolkit article first:
npm install -g karma
After you try to call karma though, you'll get unknown command error, which can be easily solved by installing karma command-line interface (CLI):
npm install -g karma-cli
To initiate the framework, just run karma init and follow the steps.
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next quest
ion.
> Chrome
>

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> spec/*.js
> src/*.js
>

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes
Please notice the positive answer for the last question. If you running Karma stand alone, it should be set to true. On contrary it can be integrate with IDE and be executed from there, for instance WebStorm. Watch a very short tutorial about the integration of Karma with WebStorm. In the end of the initiation process, a configuration file karma.conf.js will be created, which will be used to run our tests. If you didn't specify the browsers or wanted to add another one, on which you'd like to run your tests, you can install them later from the list of supported browser launchers. To add Firefox support, run the following command to install it and add Firefox key to the browsers array in the configuration file:
npm install -g karma-firefox-launcher
Last touch is needed to be completely ready to go. Karma is a framework, which executes Jasmin's spec using karma-jasmine plugin. Once you specify it in the initiation process, the plugin is downloaded and installed in the global repository. However it installs 1.3 version and not the latest 2.0. To fix this, we'll download the correct version manually:
npm install -g karma-jasmine@2_0

Running the tests


We'll be using our test specs from our previous Jasmine article and the source code can be found in GitHub.
karma start karma.conf.js
INFO [karma]: Karma v0.12.21 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 36.0.1985 (Windows 8.1)]: Connected on socket DOFTiS5FuL20HriB6iUZ
with id 19929084
Chrome 36.0.1985 (Windows 8.1): Executed 5 of 5 SUCCESS (0.021 secs / 0.006 secs
Pardon my using Windows 8 - I had to give my Ubuntu laptop for repair :) Anyway, you'll notice your browser being stuck and closing it will just rerun all the tests. To fix this, set singleRun property in your configuration file to true. Also be aware of the localhost:9876 address. It is used by Karma to run the specs, which means you can test it on any device connected to your network. Try pointing your phone’s browser to Karma by looking at the URL of one of the browser windows running the tests. Because Karma is running an instance of Node.js, your test machine is acting like a server and will send the tests to any browser that is pointed to it.

We'll talk about using Jasmine with your server side using jasmine-node package in the next article.
Sunday, July 6, 2014

Sass


Remember in our first JavaScript Developer Toolkit article I've told that you would need to install Ruby. Today we'll see why and the reason is Sass.

Sass is an extension of CSS3 that helps you creating better stylesheets with less effort. Sass frees you from repetition and gives you tools to be creative. Since you can implement changes much faster, you’ll be able to take your design in a more boldly manner. Your stylesheets will be able to keep pace with changes, all while producing standards-based CSS you can use in any environment. As mentioned already, the Sass processor is written in Ruby. However Ruby environment has to be only installed on the development environment, so it doesn't matter how you write your server side. You only deploy the generated CSS files to production environment.
gem install sass
Sass offers a lot of features such as variables, nesting, importing, mixins and many more. I'll cover the most important of them, but make sure to read the reference to make the best of it.

Variables


One of the major benefits of Sass is the variables it brings to CSS. Variables allow you to name CSS values that you use repeatedly and then refer to them by name rather than repeating the value over and over. Variables can be declared globally or inside the scope of specific rule. The latter will only be active within the declared scope. As you can see it can be used even inside the rule, like we did in border.
$panel-color: #001122;
.panel {
 $width: 100px;
 width: $width;
 color: $panel-color;
 border: 1px $panel-color solid;
}

Nesting CSS rules


One of the most annoyingly repetitive aspects of CSS is writing selectors. When you’re writing a bunch of styles, that all target the same section of the page, you often need to write the same ID over and over again. Sass takes care of that by introducing nesting. Look at the following nested rules:
#content {
 article {
  div { color: #123 }
  &:hover { color: red }
 }
 aside {
  background-color: #eee
  :hover { color: red }
  border: {
   style: solid;
   width: 1px;
  }
 }
 > section { color: #555 }
}
When Sass engine runs, the nesting rules will be flattened and rewritten into regular css rules. Notice the difference between hover rules in lines 4 and 8. The engine replaces the & sign by the parent object, article, generating article:hover, whereas plain hover is appended to the parent generating article :hover. Sass also supports sibling and child selectors as well as nesting of CSS properties, border rule in our example.
#content > section { color: #555 }
#content article div { color: #123 }
#content article:hover { color: red }
#content aside {
 background-color: #eee;
 border-style: solid;
 border-width: 1px;
}
#content aside :hover { background-color: #eee }

Mixin


When you have a few small stylistic similarities throughout your site, colors and fonts that you use consistently, variables are a great way to keep track of them. But when your styles get more complicated, you need to be able to reuse more than just individual values. That's when it's time to use mixin. It groups the rules into one named block, to which we can pass parameters and make it even more flexible.
@mixin rounded-corners(
 $radius: 5px) {
 -moz-border-radius: $radius;
 -webkit-border-radius: $radius;
 border-radius: $radius;
}
.notice {
 border: 2px solid #00aa00;
 @include rounded-corners(6px);
}
The rule above will be transformed into this:
.notice {
 border: 2px solid #00aa00;
 -moz-border-radius: 6px;
 -webkit-border-radius: 6px;
 border-radius: 6px;
}
I hope you found some interest in the Sass and we'll give it a change. Truth is - once you do, you'll never let it go. Next time we'll talk about Compass - a cookbook of various CSS recipes.