CoffeeScript in Tizen development

Introduction

CoffeeScript is a language that compiles into JavaScript. It sounds simple and it really is. All you need to know about the language and the whole ecosystem is described in detail at the CoffeeScript official website. You can build software easier and with more fun by using it. From this article you will learn how to use CoffeeScript together with Tizen. We will go through the installation process, configuration and the use of the tools. After you read this article you will be able to develop applications for the Tizen platform using CoffeeScript with ease, so let's go!

Requirements

Things you have to know or should have installed before you begin the survey.

  • Basics of JavaScript and CoffeeScript languages,
  • Node.js and npm,
  • Grunt

Before you start

CoffeeScript is first of all a way of thinking. It's better to think about what you are going to do than how to do it. CoffeeScript allows you to do so by adding some syntactic sugar to your code. There are many useful built­ in capabilities like inheritance, easy function binding, complete switch statements which behave like those in Ruby. Of course you can always embed the JavaScript code directly using apostrophes, but why do so? Let's have a look at some interesting features CoffeeScript gives you:

CoffeeScript JavaScript
Loops with conditions
places = ['madrid', 'warsaw', 'moscow']

visit place for place in places when place isnt 'moscow'
for (k = 0, len = places.length; k < len; k++) {
  place = places[k];
  if (place !== 'moscow') {
    visit(place);
  }
}
Array Slicing
numbers = [1, 2, 3, 4]
begin = numbers[0..1]
center = numbers[1...­1]
var numbers, start, center;
numbers = [1, 2, 3, 4];
start = numbers.slice(0, 2);
middle = numbers.slice(1, ­1);
Existential Operator
summer = true if sun? and not cold?
var summer;
if (
    (typeof sun !== "undefined" && sun !== null) &&
    (typeof cold === "undefined" || cold === null)) {
  summer = true;
}

There are many other add­-ons,  which make working with CoffeeScript enjoyable. Don't hold back and try them all out!

Installation and configuration

Please take into account how the project structure looks like:

.
├── Gruntfile.coffee
├── node_modules
│ ├── grunt
│ ├── grunt­closure­soy
│ ├── grunt­contrib­clean
│ ├── grunt­contrib­coffee
│ ├── grunt­contrib­concat
│ ├── grunt­contrib­copy
│ ├── grunt­contrib­cssmin
│ ├── grunt­contrib­uglify
│ ├── grunt­contrib­watch
│ ├── grunt­soy­compile
│ └── grunt­zipup
├── package.json
├── README.md
├── Tizen
│ ├── coffee
│ │ ├── app
│ │ └── app.coffee
│ ├── config.xml
│ ├── css
│ │ └── style.css
│ ├── icon.png
│ ├── index.html
│ ├── js
│ │ ├── app
│ │ ├── app.js
│ │ └── images
│ ├── lib
│ │ ├── gear
│ │ └── requirejs
│ ├── res
│ │ └── xml
│ └── temp
├── tizen_build
└── tools
  └── server.js

The starting point for our project is the package.json configuration file, as you can see in the code fragment below. This file is located­ in the project root directory. It­ is used by npm to store meta data for projects published as npm modules. So simple 'npm install' from the top directory will install every plugin we have listed. We will use Grunt - The JavaScript Task Runner and other dependencies like grunt­contrib­coffee and grunt­contrib­watch. Please do not be concerned about the version of the packages in the sample code below.

{
  "name": "project­name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt­contrib­watch": "~0.4.3",
    "grunt­contrib­coffee": "~0.9.0"
    "grunt­contrib­uglify": "~0.2.2",
    "grunt­contrib­coffee": "~0.9.0",
    "grunt­contrib­copy": "~0.5.0",
    "grunt­contrib­clean": "~0.5.0",
    "grunt­contrib­cssmin": "~0.7.0",
    "grunt­zipup": "~0.1.6"
  }
}

You can also initiate the project by invoking the 'npm init' command, which will create for you the needed package.json file. You are only obliged to add the proper content, especially plugins I have mentioned earlier.

The next important configuration file is the gruntfile. In the documentation of grunt in­ the Gruntfile.js or Gruntfile.coffee file there is a valid JavaScript or CoffeeScript file that belongs to the root directory of your project, next to the package.json file. It should be committed along with your project source. In our project we wiil use naturally the coffee format of the gruntfile.

A sample gruntfile may looks like this:

module.exports = (grunt) -> 

  # Constants 
  PROJECT_PATH = '.' 
  APP_PATH = "#{PROJECT_PATH}/Tizen" 
  BUILD_PATH = "#{PROJECT_PATH}/tizen_build" 

  # Project configuration. 
  grunt.initConfig 
    # Metadata. 
    pkg: grunt.file.readJSON 'package.json' 
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 
       '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 
    '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + 
  '* Copyright (c) <%= grunt.template.today("yyyy") %> 
        <%= pkg.author.name %>;' + 
       ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n' 

    tizen_configuration: 
      tizenAppScriptDir: '/tmp/' 
      configFile: "#{BUILD_PATH}/config.xml" 
      sdbCmd: '<YOUR-PATH-TO>/tizen-wearable-sdk/tools/sdb' 

    # Task configuration. 
    clean: [BUILD_PATH] 

    copy: 
      images: 
        expand: true 
        cwd: "#{APP_PATH}" 
        src: ['images/**'] 
        dest: "#{BUILD_PATH}/" 
      html: 
        expand: true 
        cwd: "#{APP_PATH}" 
        src: ['*.html'] 
        dest: "#{BUILD_PATH}/" 
      tizen: 
        expand: true 
        cwd: "#{APP_PATH}" 
        src: ['config.xml', 'icon.png'] 
        dest: "#{BUILD_PATH}/" 

    cssmin: 
      minify: 
        expand: true 
        cwd: "#{APP_PATH}/css/" 
        src: ['*.css', "**/*.css"] 
        dest: "#{BUILD_PATH}/css/" 
        ext: '.css' 
        
    zipup: 
      wgt: 
        appName: '<%= pkg.name %>' 
        version: '<%= pkg.version %>' 
        suffix: 'wgt' 
        files: [ 
          cwd: "#{BUILD_PATH}" 
          expand: true 
          src: '**' 
        ] 
        outDir: "#{BUILD_PATH}" 

    uglify: 
      options: 
        banner: '<%= banner %>' 
        mangle: yes 
        preserveComments: false 
        compress: 
          drop_console: yes 
      all_js: 
        files: [{ 
          expand: true 
          cwd: "#{APP_PATH}/js" 
          src: '**/*.js' 
          dest: "#{BUILD_PATH}/js" 
        }, { 
          expand: true 
          cwd: "#{APP_PATH}/lib" 
          src: '**/*.js' 
          dest: "#{BUILD_PATH}/lib" 
        }] 


    coffee: 
      options:   
        bare: yes 
      multiple: 
        expand: yes 
        flatten: no 
        cwd: "#{APP_PATH}/coffee/" 
        src: ['*.coffee', "**/*.coffee"] 
        dest: "#{APP_PATH}/js/" 
        ext: '.js' 

    watch: 
      coffee: 
        files: ["#{APP_PATH}/coffee/*.coffee", "#{APP_PATH}/coffee/**/*.coffee"] 
        tasks: ['coffee:multiple'] 

  # These plugins provide necessary tasks. 
  grunt.loadNpmTasks 'grunt-contrib-uglify' 
  grunt.loadNpmTasks 'grunt-contrib-watch' 
  grunt.loadNpmTasks 'grunt-contrib-coffee' 
  grunt.loadNpmTasks 'grunt-contrib-copy' 
  grunt.loadNpmTasks 'grunt-contrib-clean' 
  grunt.loadNpmTasks 'grunt-contrib-cssmin' 
  grunt.loadNpmTasks 'grunt-zipup' 

  # Tasks. 
  grunt.registerTask 'build', [ 
      'clean' 
  	'uglify' 
  	'cssmin' 
  	'copy' 
  	'zipup' 
  ] 
  grunt.registerTask 'compile', ['watch'] 
  grunt.registerTask 'default', ['build']  

Using a configured gruntfile like in the sample above,  we are able to use two grunt tasks. The 'default', which will build the whole project, and 'compile' – used during the development process, to examine changes in the files and directories.

How to use it

Writing

The Coffee plugin we use allows us to compile coffee formated files into their JavaScript equivalents. The compiling process takes place after using the 'compile' task and should be run by the 'grunt compile' command from the project root directory. These processes have to be running constantly until the end of your work session in the IDE. Editing any CoffeeScript file will result in creating JavaScript equivalents in the Tizen/js directory in our project. The Tizen/js directory should never be modified. The content of the folder is always automatically created during the development process. Also making changes in files inside isn't the best idea.

Here you can see how parts of the code may look like - The CoffeeScript version and the code transferred to JavaScript:

class Observable

    # event with optional timeout
	@event: (timeout, definition) ->
		if typeof timeout is 'function'
			definition = timeout 
			timeout = 0
		@_events ?= []
		@_events.push [timeout, definition]
			
	constructor: ->
		@listeners = {}
		for event in @constructor._events ?= []
			if event[0] > 0
				setTimeout event[1].bind(@), event[0]
			else
				event[1].call @
			
	add_listener: (name, timeout, callback) ->
		if typeof timeout is 'function'
			callback = timeout 
			timeout = 0
	
		(@listeners[name] ?= []).push [timeout, callback]
			
	fire_event: (name, params...) ->
		for callback in @listeners[name] ? []
			if callback[0] > 0
				setTimeout callback[1].bind(@, params...), callback[0]
			else
				callback[1].call @, params...

The compiled JavaScript output:

var __slice = [].slice;
var Observable = (function() {
  Observable.event = function(timeout, definition) {
    if (typeof timeout === 'function') {
      definition = timeout;
      timeout = 0;
    }
    if (this._events == null) {
      this._events = [];
    }
    return this._events.push([timeout, definition]);
  };

  function Observable() {
    var event, _base, _i, _len, _ref;
    this.listeners = {};
    _ref = (_base = this.constructor)._events != null ? _base._events : _base._events = [];
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      event = _ref[_i];
      if (event[0] > 0) {
        setTimeout(event[1].bind(this), event[0]);
      } else {
        event[1].call(this);
      }
    }
  }
  Observable.prototype.add_listener = util.return_this(function(name, timeout, callback) {
    var _base;
    if (typeof timeout === 'function') {
      callback = timeout;
      timeout = 0;
    }
    return ((_base = this.listeners)[name] != null ? _base[name] : _base[name] = []).push([timeout, callback]);
  });
  Observable.prototype.fire_event = function() {
    var callback, name, params, _i, _len, _ref, _ref1, _ref2, _ref3, _results;
    name = arguments[0], params = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
    _ref1 = (_ref = this.listeners[name]) != null ? _ref : [];
    _results = [];
    for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
      callback = _ref1[_i];
      if (callback[0] > 0) {
        _results.push(setTimeout((_ref2 = callback[1]).bind.apply(_ref2, [this].concat(__slice.call(params))), callback[0]));
      } else {
        _results.push((_ref3 = callback[1]).call.apply(_ref3, [this].concat(__slice.call(params))));
      }
    }
    return _results;
  };
  return Observable;
})();

It is relatively easy to see differences in size of the source code with a predominance of the CoffeeScript version. It uses less code, it is smarter and easier to understand than the generated JavaScript file. The compiled output is easy to read and pretty-printed. It is also more understandable for people who come from other standard object oriented languages like Python and Ruby. In fact CoffeeScript is inspired by those two languages.

Testing

You should treat our CoffeScript compiled project as a standard Tizen web application. It means that the testing process is done without the need to know everything about our earlier CoffeeScript project version. You will see that the simple run and debug commands from the Tizen IDE should work with your code like a charm.

Packaging

As you can see there is a dedicated 'build' task in our gruntfile (simply use the 'grunt build' from the terminal to make it work). This task consists of few other core tasks like 'clean', 'uglify', 'cssmin', 'copy' and 'zipup', which should be understandable for any developer. First of all clean the build path, then run the uglify plugin - a JavaScript parser, compressor and beautifier. The CSS files are also minified and then everything is copied to the build directory. At the end of the build process a WGT file, which is really a zipped application, is created. At this point we are ready to deploy the package to the device or to the application store.

Summary

While reading this article you can obtain the knowledge how to easily configure a project for CoffeeScript development. The basic configuration consists of the proper configuration of the npm and grunt modules, while maintaining the proper directory structure. It is relatively easy to create, maintain, run or debug a project with CoffeeScript. An additional benefit is that you get a tool for faster Tizen web application development.