OrthoCoders

You can code it, I can help!

The Case for CoffeeScript

I have been doing presentations about CoffeeScript for quite a while, now I feel it is time to put it in writing.

In the spirit of Peter Parker, Pepper Pots, Reed Richards and so many others I present you a language that I believe could change forever the way you develop web applications.

What is CoffeeScript?

From coffeescript.org here is the definition:

CoffeeScript is a little language that compiles into JavaScript. 
Underneath all those awkward braces and semicolons, JavaScript has always had a gorgeous 
object model at its heart. 
CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.

So why another language, what is wrong with one of the most popular languages around the world (if not the most popular) for web development?

  • Too verbose (too many { and } )
  • Global Variables
  • Lacks support for classes
  • Hard to make inheritance
  • Automatic type conversion between strings and numbers
  • NaN is not a number, however it is a number

I have been using CoffeeScript for more than a year and I am really happy with it. It helps to be more concise and descriptive. Plus using classes_ is a huge benefit when implementing any kind of business rules or logic related to the view.

How does CoffeeScript work? It depends on your tool of choice, but usually most tools convert the Coffeescript files into JavaScript files without requiring any manual confirmation.

Let’s explore together the good, the bad and why you should start using it!

String Interpolation

Inspired by Ruby, Python and others CoffeeScript lets you use interpolation inside a double quoted string to expand expressions without the need of using the + operator.

For interpolation we use the pound # and the brackets { }.

Use an expression inside a string
1
2
3
4
5
6
7
8
9
10
console.log "The result is #{3 + 7}"
# Prints to the console "The result is 10"

id = 5
console.log "GET request to /movies/#{id}"
# Prints to the console "GET request to /movies/5"

# Single quotes would not evaluate any expressions
console.log "GET request to /movies/#{id}"
# Prints to the console 'GET request to /movies/#{id}'

As you can see from the example above, there is no need to declare variables using var.

Functions

Functions are a first class citizen in CoffeeScript and quite easy to define and use.

The arrow/lambda defines functions

Define functions and using them
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# No parameters, no parenthesis
justLogIt = -> console.log "Just log it!"
console.log "Logging the night away #{justLogIt()}"

# Square of a number
square = (x) -> x * x
console.log "The square of 2 is #{square(2)}"

# Parenthesis are optional when passing parameters
storageDelete = (id, flag) -> console.log "Ajax call with #{id} using #{flag}"
storageDelete movieId, true

# The return is implicit, the last statement is the return value
isOdd = (x) -> x % 2 == 1
console.log "What about number 3? #{isOdd 3}"

# More than one line is ok, using indentation, no need for begin - end
deleteMovie = (e) ->
  movieId = $(e.target).data('id')
  storageDelete movieId

# However you can use the return keyword if you need it
deleteMovie = (e) ->
  movieId = $(e.target).data('id')
  return if invalidId(movieId)
  storageDelete movieId

Declaring Objects

A hash can be used to declare an object with properties.

Declaring objects and using them
1
2
3
4
5
6
7
8
9
10
11
# Declared using indentation
config =
  local:
    user: 'dev'
    pwd:  'dev123'
  remote:
    user: 'superdev'
    pwd:  "impossibleToGuess"

console.log "The local user is #{config.local.user}"
console.log "The remote user is #{config.remote.user}"

Arrays

Using arrays
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Arrays are declared with “[ ]”
deploy = ['local', 'remote', 'uat']
fib = [1, 3, 5, 8, 13, 21]

# Slicing
first = fib[0..3]
noLast = fib[0..-2]

# Using multiple assignments
[first, nick, last] = ['Jason', 'Agent', 'Bourne']

console.log "I just saw #{first} #{last} AKA (#{nick})"
# Prints "I just saw Jason Bourne AKA (Agent)"

# Using a splat to deconstruct the assignment
reviews = [45, 29, 21, 10, 8, 4]
[best, secondBest, theRest...] = reviews

# Assigns best = 45, secondBest = 29 and theRest = [21, 10, 8, 4]

Conditionals

Using conditionals classic and inline
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Classic if does not need parenthesis, brackets or begin end 
# use indentation for the body
if isJson
  callIndex()
  display()
else
  showMessage()

# Or use unless for the negated form
unless isJson
  showMessage()
else
  callIndex()
  display()

# Or you can use them as modifiers in the same statement
callIndex() if isJson

exit() unless validated and inContext

List Comprehensions

Part of what makes a language great is how you iterate over collections and what kind of operations come out of the box to do so. Having functions like map, filter and reduce as part of our toolbox makes our code easy to read and maintain.

Iterating over collections and common functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
environments = ['local', 'uat', 'prod']
# Iterate over a collection and print the elements
for env in environments
  console.log "The environment is #{env}"

# Convert each element (map)
isLocal = (env) -> env == 'local'
deploy = (env) ->
  shouldDeploy = isLocal env
  console.log "Deploying #{env}" if shouldDeploy
  shouldDeploy

results = (deploy env for env in environments)

# Will print true only for local environment
console.log "The results of deploying are #{results}"

# Using JavaScript map
results = environments.map deploy

# Filter over a collection
remoteEnvs = (env for env in environments when !isLocal env)

console.log "We have the remote environments #{remoteEnvs}"

# Using JavaScript filter to find the local
remoteEnvs = environments.filter isLocal

# Or Reduce the collection as a sum of the numbers
numbers = [8, 2, 8, 5, 3]

sum = numbers.reduce (a, b) -> a + b

# Or Find the first odd number (5)
firstOdd = numbers.find isOdd

Existential Operator

It is quite common to receive parameters or obtain results that could be suspected to be undefined or null. Usually you need to use conditional guards to confirm/deny those suspicions or use the and operator to ensure the expression is valid. CoffeeScript on the other hand, provides the operator ? that will apply a short circuit logic in an expression.

Using the existential operator
1
2
3
4
5
6
7
# Checks if a variable is null or undefined
question = paragraph? and not createdDate?
defaultValue ?= 5
precendence = first ? 5

# It can be used to avoid TypeError exception
extension = secondaryAddress?().phone?.extension

The Cherry on Top

Abstraction is a very powerful tool to implement complex logic. If you use an Object Oriented language you are familiar with writing classes to abstract concepts and implement behavior. Implementing classes with prototypes may be possible (after quite a bit of work) but having a Class as a first class citizen in the language makes it really easy to bring back the OOP goodness into our client side code.

Defining classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MovieRepository
  constructor: (@baseUrl) ->
  newMovie: (id) =>
    $.ajax
      url: "#{@baseUrl}/movies/create"
      success: (data) -> $(id).append data

# Notice the constructor takes a parameter with @? 
# That is an automatic property definition

repo = new MovieRepository('/movies')
console.log "The repo has base URL #{repo.baseUrl}"

# Inheritance, one class can extend another
class Shape
  constructor: (@width) ->

class Square extends Shape
  computeArea: -> Math.pow @width, 2

class Circle extends Shape
  radius: -> @width / 2
  computeArea: -> Math.PI * Math.pow @radius(), 2

Practical Examples

Example 1: Using Coffeescript with Jasmine

I wrote the Minesweeper kata in CoffeeScript using Jasmine for the TDD bit.

Any library like Jasmine, Underscore, Backbone, Knockout, etc., are fully compatible with Coffeescript because CoffeeScript compiles to JavaScript after all.

Here is what the test looks like.

A test for the Minesweeper class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
describe 'Minesweeper', ->

  beforeEach ->
    subject = new Minesweeper()

  it 'Should work with one row', ->
    given = ["1 3", "*.."].join("\n")
    expected = "Field #{}1\n*10"
    actual = subject.proximity(given)
    expect(actual).toEqual(expected)

  it 'Should work with two rows', ->
    given = ["2 3", "*..", "*.."].join("\n")
    expected = "Field #{}1\n*20\n*20"
    actual = subject.proximity(given)
    expect(actual).toEqual(expected)

  it 'Should work with suggested cases', ->
    given = [['4 4', '*...', '....', '.*..', '....'],
             ['3 5', '**...', '.....', '.*...'],
             ['0 0']].reduce((a, b) -> a.concat(b)).join('\n')

    expected = ['Field 1', '*100', '2210', '1*10', '1110'].concat(['Field 2', '**100', '33200', '1*100']).join('\n')
    actual = subject.proximity(given)
    expect(actual).toEqual(expected)

Example 2: Using Coffeescript with jQuery

Let’s imagine there is a page that lists movies in a movie library and you want to let the user change the library title without going back to the server.

My (simplified) page source would look something like this:

Movie repository title markup
1
2
3
4
5
6
7
8
9
10
11
12
<p id='movie-repository-title'>
  My Movies Repository
  <div id='title'>
    <span>Title here</span>
    <button>Change it</button>
  </div>
  <div id='edit' style='display: none'>
    <input type='text' name='title'/>
    <button class='cancel'>Cancel</button>
    <button class='save'>Save</button>
  </div>
</p>

The functionality could be summarized as follos:

  • When clicking the edit button, hide the title and show the input box
  • After typing a new title
    • If save is pressed show the new title on the page
    • If cancel is pressed hide the input box and show the old title

To implement the functionality using jQuery you need to listen to the button click event and trigger the show/hide of the edit markup section.

You could use some functions to edit, save, cancel and implement the toggle between edit and title.

But instead of writing a bunch of functions, why not just focus on one single clear resposnibility:

CHANGE THE NAME OF THE REPOSITORY

Here is a CoffeeScript class to model the title editing and implement the change (though I believe the code is pretty clear I added comments for you benefit reading CoffeeScript for the first time):

movie_repository.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MovieRepositoryTitleEditor

  constructor: (@id) ->
    # initialize two properties to contain the edit and title
    @titleView = $("##{@id} #title")
    @editView  = $("##{@id} #edit")

    # initialize the handlers for the click events
    @initializeEventHandlers()

    editTitle: =>
      # Clear the title and show the edit markup
      @newTitle ""
      @swapVisible()

    saveChanges: =>
      # Change the actual title and show the title markup
      @titleView.find("span").text @newTitle()
      @swapVisible()

    newTitle: (newVal=null) =>
      # Set or get the value of the input field
      @editView.find("input").val(newVal)

    initializeEventHandlers: =>
      # Listen when buttons are clicked
      @titleView.find('button').on 'click', @editTitle
      @buttonView.find("button.save").on 'click', @saveChanges
      @buttonView.find("button.cancel").on 'click', @cancelChanges

    swapVisible: =>
      # Toggle visibility for both edit & title
      @titleView.toggle()
      @editView.toggle()

To invoke the class you should add something like:

Creating the repository title editor instance
1
$ -> new MovieRepositoryTitleEditor 'movie-repository-title'

Or if you can not use CoffeeScript in your view markup, the JavaScript alternative would look like:

Creating the repository title editor instance
1
2
3
$(function() {
  MovieRepositoryTitleEditor.initialize('movie-repository-title');
});

What is Stopping you from using Coffeescript then?

So is it all ponies and rainbows? To be honest I do think so :)

However there are some considerations to emmmm… consider:

Compilation from CoffeeScript to JavaScript

Issue: Do I need to care for another step in my building process?

Relax: You do not need to. Most platforms include that step in the building process, asset generation or via the IDE compiling to JavaScript every time you save the _Coffeescript source file.

Debugging the Source

Issue: How am I supposed to debug the generated JavaScript?

Chilax: First, please use tests to make sure sure your code works as expected. However, sometimes you may have to look at the code. The generated code is not that bad. The variables have the same names, methods in a class will become prototypes, and you can use source maps to point your browser (Chrome) to the generated source file and load the Coffeescript file instead.

Learning

Issue: Do I need to learn another language?

Whatevs: Really? What are you afraid of? Running out of room?

Summary

CoffeeScript is a great opportunity to tidy up your JavaScript and bring OOP back to the client.

Similar ideas are TypeScript and ClojureScript.

Go online to coffeescript.org and try it, you can see the CoffeeScript source and the generated JavaScript side by side to compare.

Also this cookbook is a good place to start. Write a few Katas (again, here is mine) and enjoy the benefits of writing less code, extra functional goodness and being more descriptive.

Comments