Monday 29 April 2013

Developing Shopify Dashing on Windows with Vagrant

We've started using Shopify's Dashing gem internally so we can publicise any moving figures people are interested in. You can go from zero to great-looking-dashboard in next to no time once you understand the API. The Dashing Wiki has enough documentation and the example dashboards that ship with it are enough to communicate the usage pattern. 

This post is about the biggest stumbling block we had when starting out. We develop on Windows machines as most of what we do is .net. Dashing is a solution written in Ruby so we wanted to develop on a Linux OS as we would be deploying to Heroku to host our production dashboards.

Enter Vagrant. This a really neat tool that lets you script the creation of a virtual machine. The Vagrant script sits in the root of your project so that it is under source control and can be shared across the team. Simply open the command line at your project directory and enter vagrant up. Within minutes a virtual machine running Ubuntu in text only mode is started.

Vagrant takes care of automatically sharing the project folder on your host machine with the guest virtual machine, so that you can continue development as normal and just execute the code inside the virtual machine. The port 3030 is forwarded so that we can view the Dashboard on our local browser. This is the content of my Vagrantfile:


Vagrant.configure("2") do |config|
  config.vm.box = "precise32"
  config.vm.network :forwarded_port, guest: 3030, host: 3030
end

So now we have in waiting a virtual machine running our target operating system, with a synced folder and the ability to hit any website running inside of it. However, that's not quite enough for Dashing. We need to update the version of Ruby to 1.9.1 and add some other essential apps. Here are the necessary steps that might hopefully save someone a lot of time:


From the Windows command line:

c:\MyDashing> vagrant up
c:\MyDashing> putty


Configure putty correctly and ssh into the VM.
Log in with username: vagrant
Enter commands:

$ cd ../../
$ sudo apt-get update
$ sudo apt-get install ruby1.9.1-full -y
$ exit

Back in the Windows command line:

c:\MyDashing> vagrant reload
c:\MyDashing> putty

Again, ssh into the VM:

$ cd ../../

$ sudo apt-get install build-essential -y
$ sudo apt-get install nodejs -y
$ sudo gem install bundler --no-rdoc --no-ri
$ sudo gem install dashing --no-rdoc --no-ri

$ dashing new MyDashing
$ cd MyDashing
$ bundle install
$ dashing start


Now you have initialized a dashing project. You should be able to enter dashing start at the command prompt and not be met with any error messages. Navigate to http://localhost:3030/sample on your local machine and hey presto! Development can now continue on your local machine, while serving the dashboards via your virtual box.

Gotchas:
- Make sure you update your Gemfile with any gems used in any new job you create. Then don't forget to run bundle install at the prompt!
- Any private key files or other assets required by code should reside in the root of the project.
- Keep an eye on the output when running vagrant up as Vagrant will cleverly accomodate any conflicting forwarded ports. This can affect the ports you use to ssh into the VM and view the dashboard.

P.s. I'm aware of the provisioning facilities of Vagrant, including the capability of running all of the above through a shell script during construction of the VM. However, I couldn't get it to work without including the restart so I have just left the raw commands as a the guide for now. If/when I figure it out I will update the post. However, these steps only need to be run once per machine (not every time you run vagrant up) so you might find its just about tolerable as it is.


Thursday 18 April 2013

AngularJS Directives and Isolate Scope

I've been learning the AngularJs framework recently and have come across a case study that highlights how Isolate Scope works.

The app we're developing needs two individual sliders. The values represented by each of the sliders are related as they form two variables in a simple equation: the Loan-to-value ratio of a mortgage (LTV). The LTV is a percentage of the Loan Required figure over the Property's Value figure. There is a slider for both the Loan Required and Property Value.

Currently, mortgage lenders are reluctant to offer mortgages with an LTV over 95%. We've therefore chosen to introduce this feature into our sliders, so that they may not be dragged to a position in which the LTV ration is greater than 100%. If a slider is dragged beyond the 100% mark, the other slider must be moved accordingly so as to maintain the ratio at 100%. Challenge accepted.

This seemed like a good fit for a Directive. A Directive in AngularJs is a way of describing a portion of the page. Using a directive to construct your page is what's known as being declarative. Your intentions for the purpose of that piece of mark up is clear. A Directive would be something that you might use often, so as to provide yourself with a short hand way of getting the element on the page.

We intend to create a 'Linked Slider' Directive, that is capable of communicating with other slider directives, so that their values may stay in-sync. To do this we must make use of Isolate Scope. Scope is very important in AngularJs. It is the 'glue' that allows controllers and views to share information. The Angular Magic permits us to manipulate the same values in both the controller and the view - its the Scope that keeps the values consistent across both, thus delivering a huge amount of value for developers.

Scope is normally associated with a Controller and a View. Directives are declared on the View and share the same Scope as the Controller and View. Isolate Scope enables the Directive to maintain its own Scope. This is useful for us for two reasons. Firstly, it gives us the ability to 'wire up' our directive to our outer Scope without tightly coupling it - making it reusable elsewhere. Secondly, it allows us to have multiple instances of the same slider Directive.

You might enjoy the working example to get a feel for what I'm talking about. The code for the directive is shown below, followed with discussion..


Starting from line 1, the directive is declared on the app global variable, and named as 'linkedSlider'. It is restricted for us as an attribute (A), and then has its Scope defined.

The Scope property is the Isolate Scope I was banging on about. There is a convention at play here. The keys of the scope property become attributes that can be used to configure the Directive when it is declared in the mark up. The '=', '@' and '&' values of these keys have special meaning.

'=' means the value is a two-way binding property. This means that its value can be changed by the Directive and by the Controller. This is why we use the '=' symbol to describe both the Value and Link properties. The Value property is the value that the slider is going to maintain. The Link property is the value that the slider is going to have to update in light of changes to its own Value when the LTV is pushed over 100%. It is this arrangement that lets us declare the property once and but be able to use it as many sliders as we like.

'@' is a straight forward attribute. It does not stay up to date in the Directive if it is changed in the Controller's scope. For this reason attributes are normally populated with string literals or numeric values. In our case, we specify all the necessary properties of the slider (max, min & step).

'&' represents an expression to be evaluated. It could be a pointer to a function on the Scope, or anything that can be executed. I have used this type of scope property to contain the test to be checked each time the slider is moved, to determine whether or not the other slider needs to be adjusted.

The Link function that follows is what binds the directive to the controller and kicks everything off. You will notice still the reliance on jQuery to create an instance of the jQuery UI slider. Nothing unusual there. As you can see I pass in the necessary configuration from the Isolate Scope of the instance of the Directive. When the slide event occurs, I have to call the special function scope.$apply() for the value change to be recognised in the Controller Scope. I then evaluate the Scope's condition property (remember that we defined it as an expression with '&') to find out if the new value would cause the LTV to exceed 100%. If so, I use the scope.$apply function again to update the Link property value in the Controller Scope.

As we have just seen, we have given our Directive the ability to change the value of it's Link property. This Link property will be a Value property in another instance of the same Directive, so we need to be able to listen for changes. scope.$watch is just the thing we need to be able to react to changes to our Value in the Controller Scope. When a change occurs, we use the jQuery UI slider API to update our slider's value, this also takes care of dragging the slider to the correct position.

There still remains two unexplained features for the moment. I do not understand why a setTimeout is required. Without it, the attribute ('@') values are not yet populated at the time of initialising the JQUI Slider. Also, I am also required to apply a scope.$watch to the Link value. Again, without this the Link property value will not stay up to date, even though there is no actual function content. A small price to pay for what is otherwise an elegant (enough) solution.