CoffeeScript, An Introduction for Nodejs developers

Preview:

DESCRIPTION

 

Citation preview

|

COFFEESCRIPTAN INTRODUCTION FOR NODE DEVELOPERS

BY: MEHDI VALIKHANISOFTWARE ENGINEER @ ALPHATISE

Follow me: @mehdivk mehdi@alphatise.com

IT'S JUST JAVASCRIPTCOFFEESCRIPT IS A PROGRAMMING LANGUAGE THAT

TRANSCOMPILES TO JAVASCRIPT.

SYNTACTIC SUGAR TO JAVASCRIPT

IT'S MATURE AND PRODUCTION-READY

DEVELOPED BY: JEREMY ASHKENAS

INITIAL COMMIT: DECEMBER 13TH, 2009FIRST RELEASE: DECEMBER 24H, 2009

VERSION 1.0: FEBRUARY 2010CURRENT VERSION: 1.8

WHO'S USINGCOFFEESCRIPT?

DROPBOXGITHUB

WRITE LESS, DO MOREHUMAN-READABLE CODE

STAY AWAY FROM WEIRD JS BEHAVIOUR

SAY GOODBYE TO PROTOTYPE! USE CLASSES

ANY MANY MORE ...

COFFEESCRIPT SYNTAX

NO MORE VAR ; ( )

async = require 'async' console.log async.VERSION

var async = require('async'); console.log(async.VERSION);

FUNCTIONSIT'S PART OF OUR EVERYDAY LIFE IN JS WORLD

LET'S BE AGILE!

addUser = (user, cb) -> model = new User #DO SOMETHING model.save (err, result) -> cb null, result

var addUser = function(user, cb) { var model = new User(); //DO SOMETHING model.save(function(err, result){ cb(err, result); }); }

IMPLICIT RETURN

someFunction = (param1, param2) -> param1 * param2 * param2 console.log someFunction 100, 200

var someFunction; someFunction = function(param1, param2) { return param1 * param2 * param2; } console.log(someFunction(100, 200));

OBJECTSGET RID OF THOSE { } ,

bus = routeNumber: 273 from: 'Wynyard' to: 'Chatswood' via: 'North Sydney, Pacific Highway'

var bus = { routerNumber: 273, from: 'Wynyad', to: 'Chatswood', via: 'North Sydney, Pacific Highway' }

query = status: $in: ['active', 'pending_for_approval'] payment: $nin: ['invoice', 'free'] tc: $exists: true

var query = { status: { $in: ['active', 'pending_for_approval'] }, payment: { $nin: ['invoice', 'free'] }, tc: { $exists: true } }

STRING INTERPOLOATION

meetup = 'Node' msg = "#{meetup} meeup is awsome!" console.log msg

var meetup = 'Node'; var msg = meetup + ' meetup is awsome!'; console.log(msg);

CLASSESBETTER INHERITANCE WITHOUT PROTOTYPE

WE SHOULDN'T USE LIBRARIES TO HAVE CLASSES IN JS

class Cache generateCacheKey: (key) -> throw new Error 'Not Implemented' getPeriod: -> 120 * 60 store: (key, data) -> throw new Error 'Not Implemented' clearCache: (key) -> #DO SOMETHING

class RedisCache extends Cache generateCacheKey: (key) -> "redis_cache_#{key}" store: (key, data) -> period = @.getPeriod() #STORE IN REDIS

DESTRUCTURING OBJECTS

{Router} = require 'express' router = new Router

var express = require('express'); var Router = experss.Router; router = new Router();

DESTRUCTURING ARRAYS

student = 'Mehdi Valikhani' [firstName, lastName] = student.split ' '

var student, ref, firstName, lastName; student = 'Mehdi Valikhani'; ref = student.split(' '); firstName = ref[0]; lastName = ref[1];

DESTRUCTING ARGUMENTS

calculator = ({width, height}) -> width * height options = width: 10 height: 5 alert calculator options

EXISTENTIAL OPERATOR ?REMOVE THOSE NULL OR UNDEFINED CHECKES FROM CODE

if err? #log to logger cb err

if (typeof err !== "undefined" && err !== null) //log to logger cb(err);

INSTALL COFFEESCRIPTnpm install -g coffee-script

EXECUTE A SCRIPTcoffee /path/to/script.coffee

COMPILE A SCRIPTcoffee -c /path/to/script.coffee

NODE + COFFEEFROM SCRATCH

REST API DEVELOPMENT USING EXPRESS + NODE + COFFEE

CODE STRUCTURE

+ lib + books Book.coffee //Our Mongoose Model router.coffee //router for books API+ node_modules + body-parser //from JSON responses + coffee-script //compiler of Coffee files + express //express frameworkapp.coffee //express app configpackage.json // NO DESCRIPTIONindex.js //ACTUAL MAGIC HAPPENS HERE!

app.coffee

bodyParser = require 'body-parser'express = require 'express'userRouter = require './lib/books/router'

app = express()app.use bodyParser.json()app.use '/users', userRouter

module.exports = app

lib/books/router.coffee

{Router} = require 'express'Book = require './Book'router = new Router

fetchBook = (req, res, next) -> req.paramBook = Book next()

getBook = (req, res) -> res.json(req.paramBook)

router.route('/:book_id') .all(fetchBook) .get(getBook)

module.exports = router

index.js

require('coffee-script/register'); var app = require('./app'); app.listen(1984, function(){ console.log('App is available via http://127.0.0.1:1984'); });

RUN THE APPnode index.js

nodemon -e js,coffee index.js

NODE + COFFEE

AN EXISTING PROJECT

ADD FOLLOWING CODE TO YOUR STARTER FILErequire('coffee-script/regiter');

THAT'S IT!Add .coffee files, write your code in coffeerequire other .js or .coffee files as always

RUN THE APPnode index.js

nodemon -e js,coffee index.js

DEBUGGINGCOFFEESCRIPTAPPLICATIONS

USE "DEBUGGER" AS ALWAYS

fetchBook = (req, res, next) -> debugger req.paramBook = Book next()

node debug index.js

BUT DON'T FORGET!in debug mode, you will be faced with compiled

JS code

break in lib/books/router.coffee:11 9 10 fetchBook = function(req, res, next) { 11 debugger; 12 req.paramBook = Book; 13 return next(); debug> repl Press Ctrl + C to leave debug repl > req.params.book_id '123' >

JAVASCRIPT SCOPINGin JavaScript, the scope of a variable is

defined by its location within the source code(it is apparent lexically) and nested

functions have access to variables declared intheir outer scope. - mozilla.org

Question: What's result of running this code?

name = 'Alex'

greeting = -> name = 'James' console.log "Hey #{name}!"

greeting() console.log "Hey #{name}!"

A:> Hey James!> Hey James!

B:> Hey James!> Hey Alex!

"A" IS CORRECT ANSWER> Hey James!> Hey James!

THIS IS COMPILED-TO-JS VERSION OF ABOVECODE

var greeting, name;

name = 'Alex';

greeting = function() { name = 'James'; return console.log("Hey " + name + "!");};

greeting();

console.log("Hey " + name + "!");

TWO IMPORTANT FACTS ABOUT COFEESCRIPT SCOPING

Variable shadowing is not allowed

variable is created at the moment of the first assignment to it

"VARIABLE SHADOWING IS NOT ALLOWED"

DOESN'T MEAN

YOU CAN'T DEFINE A VARIABLE MORE THANONCE IN DIFFERENT FILES

IT MEANS

YOU CAN'T DEFINE IT MORE THAN ONCE IN ASINGLE FILE

SO WHAT SHOULD I DO IF I WANT TO HAVESHADOWS?

It's not a good practice, don't do that!

It decreases readbility of your code!

Brings more confusion to your code

With shadowing you can't access our variables anymore (withsame name)

USE `` TO BRING VAR BACK

name = 'Alex'

greeting = -> ̀ ` name = 'James' console.log "Hey #{name}!"

greeting() console.log "Hey #{name}!"

var name

It a kind of hack! Don't do that!

SHADOW IT USING FUNCTION PARAMETER

name = 'Alex'

greeting = (name = '')-> name = 'James' console.log "Hey #{name}!"

greeting() console.log "Hey #{name}!"

Better first solution!

USE COFEESCRIPT'S CLOSURE FEATUREThis is much much better!

name = 'Alex'

greeting = -> do (name = '') -> name = 'James' console.log "Hey #{name}!"

greeting() console.log "Hey #{name}!"

MORE INFO ABOUT LEXICAL SCOPING:

https://github.com/raganwald-deprecated/homoiconic/blob/master/2012/09/actually-YOU-dont-understand-lexical-scope.md

https://github.com/raganwald-deprecated/homoiconic/blob/master/2012/09/lexical-scope-in-coffeescript.md

USEFUL RESOURCESYour best friend

Don't translate CoffeeScript to JS, try to learn as new langage

Teaches CoffeeScript as new language instrad of translating toJS

Best Practices

CoffeeScript Docs

CoffeeConsole: A Google Chrome Extention

CoffeeScript Ristretto

The Little Book On CoffeeScript

CoffeeScript Style Guide

Thank you!