inaktivitet som vanligt

Som jag trodde, efter att kursen vart avslutad så har jag inte skrivit någonting alls här på bloggen. Visste egentligen att det skulle bli så men vad ska man göra, är väl helt enkelt för att jag känner att jag inte har något vettigt att skriva om.

Håller förnärvarande på med examensarbete på C-nivå med Qben, det går framåt även om det känns som att vi har slackat en hel del men vi kommer bli klara, no doubt!

steg 05 – utvärdering

Då har vi kommit till utvärderingen utav kursen, projektet och allt där emellan.

Min (nya) relation till Javascript

Min relation till JavaScript har förändrats en hel del sedan den första kursen inom det, och nu känner jag att jag inte enbart kommer använda det genom jQuery för animeringar och effekter.

Efter att ha spenderat den här läsperioden med Backbone så har det faktiskt blivit en god kompanjon inom webbutvecklingen. Jag har implementerat ett antal olika applikationer efter att ha skrivit klart min Todo applikation för kursen, bland andra så har jag skrivit en egen kontakt applikation som finns här och så har jag skrivit ett antal andra ”lek” applikationer bara för att lära mig använda Backbone ännu mer.

Jag har även experimenterat en del med att använda MongoDB (Hosting lösning från MongoHQ) där jag lärde mig hur man kan implementera ett ”mini-api” i PHP för att CRUD mot en bakomliggande databas. Det som förvånade mig mest med detta var hur enkelt det var att implementera användandet av en bakomliggande databas i modellerna/collectionerna, endast en url egenskap behövdes sättas och så fick PHP sköta resten. Det som underlättade ännu ett snäpp till var att Backbone automatiskt skickade rätt HTTP headers (GET, POST, PUT, DELETE) så att man endast behövde kolla vilken request typ headern bestod av för att kunna utföra de olika CRUD funktionerna. Mycket Smidigt!

Kursen

Jag tycker att kursen har varit väldigt rolig, främst så har det varit väldigt givande för min relation mot Javascript men den har även hjälpt mig förstå kraften med att planera och identifiera objekt, metoder, moduler och funktioner inom en applikation innan själva implementeringen drar igång. Detta såg jag som en klar fördel, eftersom det klart blev enklare att veta hur allting skulle fungera och hur applikationen som helhet skulle se ut.

Kontakten med David har varit väldigt god, vi har flera gånger spenderat lång tid med långa samtal kring buggar, funderingar, lösningar osv. Och i vissa fall har vi båda nästan blivit skalliga hehe.

Sammanfattning

Kursen har gett mig mycket, både i planeringsstadiet och själva implementationsstadiet. Det jag känner har varit mest givande är min nya relation med Javascript och den kommer jag att fortsätta utveckla i framtiden så mycket jag bara kan, bara baka ihop allting jag kan och skapa något jag verkligen kan vara stolt över.

steg 04 – fördjupning – Backbone vs. Spine

Todo – Backbone vs. Spine

Min tanke är att använda mig utav Addy Osmanis TodoMVC projekt som är en Todo-applikation skriven i många olika ramverk och tekniker inom JavaScript. Du kan kika på projektet här. Det är fler utvecklare inblandade i projektet men Addy är lead.
Tanken är helt enkelt att jämför de olika implementationerna mellan hur applikationen är implementerad i Backbone och Spine. Jag kommer inte fördjupa mig någonting i Hur Spine används utan enkelt försöka förklara skillnaderna i varje Modell, Vy och Controller på en grundläggande nivå.

Eftersom SpineJS är CoffeScript så passar det bra att nämna min grundläggande artikel om CoffeScript.

Modell

Vi börjar med att kika på vad det är för skillnad på modellerna i Backbone respektive Spine.

Model i Backbone

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
var Todo = Backbone.Model.extend({
 
    // Default attributes for the todo.
    defaults: {
      content: "empty todo...",
      done: false
    },
 
    // Ensure that each todo created has `content`.
    initialize: function() {
      if (!this.get("content")) {
        this.set({"content": this.defaults.content});
      }
    },
 
    // Toggle the `done` state of this todo item.
    toggle: function() {
      this.save({done: !this.get("done")});
    },
 
    // Remove this Todo from *localStorage* and delete its view.
    clear: function() {
      this.destroy();
    }
 
  });

Model i Spine (Tyvärr så har itne WP-Syntax stöd för CoffeScript)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class window.Task extends Spine.Model
  @configure 'Task', 'name', 'done'
  @extend Spine.Model.Local
 
  validate: ->
    'Task name is required' unless $.trim(@name)
 
  @active: ->
    @select (task) -> !task.done
 
  @done: ->
    @select (task) -> !!task.done
 
  @destroyDone: ->
    task.destroy() for task in @done()

Backbone modellen ser ganska identisk ut med hur vi har gjort under kursen med våra egna applikationer så där behöver väl jag inte nämna någonting speciellt, förutom ett annat sätt att sätta default-värden på modellerna vilket jag inte visste att man kunde göra på detta vis. Vi kan även notera här att @ används istället för att this (t.ex. this.egenskap => @egenskap). Även om syntaxen verkar vara lite klurig att läsa/förstå så går det enkelt när man har läst på om hur CoffeScript fungerar och dess syntax.

Router/Controller

Vad jag kan se i Backbone implementationen av Todo applikationen så finns det ingen router, så denna kan jag inte göra någon jämförelse mot, däremot kan jag nämna att SpineJS använder sig utav controllers.

app.coffe – Huvudcontroller för applikationen

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
36
37
38
39
40
41
42
43
44
45
46
class TaskApp extends Spine.Controller
  elements:
    '.items':                         'tasks'
    '.countVal':                      'counter'
    'a.clear':                        'clearCompleted'
    'form#new-task input[name=name]': 'newTaskName'
 
  events:
    'submit form#new-task': 'new'
    'click a.clear':        'clearCompleted'
 
  constructor: ->
    super
    Task.bind 'create', @renderNew
    Task.bind 'refresh', @renderAll
    Task.bind 'refresh change', @renderCounter
    Task.bind 'refresh change', @toggleClearCompleted
    Task.fetch()
 
  new: (e) ->
    e.preventDefault()
    Task.fromForm('form#new-task').save()
    @newTaskName.val('')
 
  renderNew: (task) =>
    view = new Tasks(task: task)
    @tasks.append view.render().el
 
  renderAll: =>
    Task.each @renderNew
 
  renderCounter: =>
    @counter.text Task.active().length
 
  toggleClearCompleted: =>
    if Task.done().length
      @clearCompleted.show()
    else
      @clearCompleted.hide()
 
  clearCompleted: ->
    Task.destroyDone()
 
 
$ ->
  new TaskApp(el: $('#tasks'))

Efter att ha kikat på koden en snabbis så framgår det att upplägget för applikationen i både Backbone och Spine är väldigt likt. Controllern börjar med att definera vilka element som den ska använda sig utav, definerar events och har en constructor (motsvarighet till Backbones initialize) där han binder event för Task-objektet (De verkar inte finns någon motsvarighet till Backbone collections i Spine). Sedan är det helt vanliga funktioner enligt vanlig Backbone standard kan man säga. Vad jag kan se så händer det inget här som man inte kan förstå. Det ända jag kan notera i controllern är att den faktiskt mer liknar en Backbone vy än en Backbone router.

Vyer

Vi vet alla hur en vy i Backbone ser ut, men för att förenkla jämförelsen så kommer AppView:n här.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Our overall **AppView** is the top-level piece of UI.
  var AppView = Backbone.View.extend({
 
    // Instead of generating a new element, bind to the existing skeleton of
    // the App already present in the HTML.
    el: $("#todoapp"),
 
    // Our template for the line of statistics at the bottom of the app.
    statsTemplate: _.template($('#stats-template').html()),
 
    // Delegated events for creating new items, and clearing completed ones.
    events: {
      "keypress #new-todo":  "createOnEnter",
      "keyup #new-todo":     "showTooltip",
      "click .todo-clear a": "clearCompleted",
      "click .mark-all-done": "toggleAllComplete"
    },
 
    // At initialization we bind to the relevant events on the `Todos`
    // collection, when items are added or changed. Kick things off by
    // loading any preexisting todos that might be saved in *localStorage*.
    initialize: function() {
      _.bindAll(this, 'addOne', 'addAll', 'render', 'toggleAllComplete');
 
      this.input = this.$("#new-todo");
      this.allCheckbox = this.$(".mark-all-done")[0];
 
      Todos.bind('add',     this.addOne);
      Todos.bind('reset',   this.addAll);
      Todos.bind('all',     this.render);
 
      Todos.fetch();
    },
 
    // Re-rendering the App just means refreshing the statistics -- the rest
    // of the app doesn't change.
    render: function() {
      var done = Todos.done().length;
      var remaining = Todos.remaining().length;
 
      this.$('#todo-stats').html(this.statsTemplate({
        total:      Todos.length,
        done:       done,
        remaining:  remaining
      }));
 
      this.allCheckbox.checked = !remaining;
    },
 
    // Add a single todo item to the list by creating a view for it, and
    // appending its element to the `<ul>`.
    addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },
 
    // Add all items in the **Todos** collection at once.
    addAll: function() {
      Todos.each(this.addOne);
    },
 
    // Generate the attributes for a new Todo item.
    newAttributes: function() {
      return {
        content: this.input.val(),
        order:   Todos.nextOrder(),
        done:    false
      };
    },
 
    // If you hit return in the main input field, create new **Todo** model,
    // persisting it to *localStorage*.
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      Todos.create(this.newAttributes());
      this.input.val('');
    },
 
    // Clear all done todo items, destroying their models.
    clearCompleted: function() {
      _.each(Todos.done(), function(todo){ todo.clear(); });
      return false;
    },
 
    // Lazily show the tooltip that tells you to press `enter` to save
    // a new todo item, after one second.
    showTooltip: function(e) {
      var tooltip = this.$(".ui-tooltip-top");
      var val = this.input.val();
      tooltip.fadeOut();
      if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout);
      if (val == '' || val == this.input.attr('placeholder')) return;
      var show = function(){ tooltip.show().fadeIn(); };
      this.tooltipTimeout = _.delay(show, 1000);
    },
 
    toggleAllComplete: function () {
      var done = this.allCheckbox.checked;
      Todos.each(function (todo) { todo.save({'done': done}); });
    }
 
  });

En helt vanlig Backbone vy.

Nu blir det lite repetition här men det är för att (som jag nämnde ovan) att controllers i Spine verkar fungera som vyer också. Nedan kommer samma controller som ovan:

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
36
37
38
39
40
41
42
43
44
45
46
class TaskApp extends Spine.Controller
  elements:
    '.items':                         'tasks'
    '.countVal':                      'counter'
    'a.clear':                        'clearCompleted'
    'form#new-task input[name=name]': 'newTaskName'
 
  events:
    'submit form#new-task': 'new'
    'click a.clear':        'clearCompleted'
 
  constructor: ->
    super
    Task.bind 'create', @renderNew
    Task.bind 'refresh', @renderAll
    Task.bind 'refresh change', @renderCounter
    Task.bind 'refresh change', @toggleClearCompleted
    Task.fetch()
 
  new: (e) ->
    e.preventDefault()
    Task.fromForm('form#new-task').save()
    @newTaskName.val('')
 
  renderNew: (task) =>
    view = new Tasks(task: task)
    @tasks.append view.render().el
 
  renderAll: =>
    Task.each @renderNew
 
  renderCounter: =>
    @counter.text Task.active().length
 
  toggleClearCompleted: =>
    if Task.done().length
      @clearCompleted.show()
    else
      @clearCompleted.hide()
 
  clearCompleted: ->
    Task.destroyDone()
 
 
$ ->
  new TaskApp(el: $('#tasks'))

Det finns även en till controller i Spine applikationen som definerar en controller för varje task-object, som fasktiskt verkar användas som en vy här innuti renderNew funtionen. Jag hade nog mer valt att kalla den för en sorts partiel vy, men det är nog för att jag kommer från Backbone och hade säkerligen tyckt att Backbone gör det konstigt om jag kommit från Spine.

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
36
37
38
39
class window.Tasks extends Spine.Controller
  ENTER_KEY  = 13
  ESCAPE_KEY = 27
 
  elements:
    'form.edit': 'form'
 
  events:
    'click a.destroy':            'remove'
    'click input[type=checkbox]': 'toggleStatus'
    'dblclick .view':             'edit'
    'keypress input[type=text]':  'finishEditOnEnter'
    'blur input[type=text]':      'finishEdit'
 
  constructor: ->
    super
    @task.bind 'update', @render
    @task.bind 'destroy', @release
 
  render: =>
    @replace $('#task-template').tmpl(@task)
    this
 
  remove: ->
    @task.destroy()
 
  toggleStatus: ->
    @task.updateAttribute 'done', !@task.done
 
  edit: ->
    @el.addClass('editing')
    @$('input[name=name]').focus()
 
  finishEdit: ->
    @el.removeClass('editing')
    @task.fromForm(@form).save()
 
  finishEditOnEnter: (e) ->
    if e.keyCode in [ENTER_KEY, ESCAPE_KEY] then @finishEdit()

Här känner jag även att, med utgångspunkt från min Todo-applikation, att det här borde finnas direkt på modellen och inte på en controller/vy. Men allt beror självklart på hur man väljer att implementera sin applikation.

Slutsats

Efter att ha läst på, och skrivit en artikel om grunderna i CoffeScript, och sedan kikat igenom koden för Backbone respektive Spine så känner jag att implementationerna är nästan identiska med varandra. Detta kanske beror på att de är skriva av samma person/personer men jag trodde faktiskt först att det skulle vara större skillnad syntax och struktur mässigt än vad det faktiskt var.

Jag känner att man blir lite små förvirrad över vad som egentligen är en vy och vad som är en controller i Spine, kanske faller det på valet av hur applikationen är implementerad men jag blev lite förvirrad just när det gäller dessa två typer av objekt. Enligt den här implementationen så framgår det ganska klart och tydligt att controller är en typ controller och vy, och att det inte direkt finns någon uppdelning av dessa.

Med en del Backbone kunskap och en grundläggande överblick över hur CoffeScript fungerar så kan jag klart och tydligt se att Spine implementationen klart är mycket mer lättläst och enklare att förstå. Detta beror antagligen mest på skillnaden i hur syntax ser ut i CoffeScript som Spine är skrivet i, det ger klara fördelar om man kan syntaxen, framför allt så sparar det en hel del tråkiga klammrar och långa filer.

Jag kommer nog definitiv att kika mer på Spine/CoffeScript och börja skriva applikationer i detta språk, men först måste jag få upp en fungerande Node server som inte riktigt gick som jag tänkt förra gången jag försökte mig på detta.

steg 04 – fördjupning – coffescript

Alla som arbetar med webben på utvecklingssidan har nog hört talas om CoffeScript, ett helt nytt sätt att skriva på. CoffeScript må ser annorlunda ut i syntaxen, men det kompileras alltid till helt vanlig ”Raw”-JavaScript. CoffeScript kommer att benämnas CS i resterande delar av artikeln.

I den här artikeln så tänkte jag bara gå igenom skillnaderna mellan CS och JavaScript, mest för utbildningssyfte men även som en fördjupning av min jämförelse mellan Todo Backbone och Todo SpineJS (Som är skrivet i CS).

Ett måste för att kunna kompilera CS är NodeJS. Jag kommer endast att gå igenom lite grundläggande syntax i CS så som variabler, funktioner, loopar, object, arrayer och villkorliga tilldelningar (If, else.. ).

CoffeScript

CoffeScript, tillskillnad från vanlig JavaScript, använder sig utan mellanslag (whitespace) för att avgränsa kod block. Man behöver inte använda sig utav semikolon (;) för att avsluta rader, och måsvingar/klammerparanteser {} blir ett minneblott då man istället använder sig av indentering. CS är helt enkelt mycket enklare att läsa utan massa kodblock med konstiga klammrar och indenteringar.

Funktioner

Alla vet vi väl hur det ser ut när man definerar en funktion i JavaScript, om inte så kommer en påminnelse här.

1
2
3
4
5
6
7
8
9
var cube, square;
 
square = function( x ) {
    return x * x;
}
 
cube = function( x ) {
    return square( x ) * x;
}

Förstår du inte detta, så har du nog kommit helt fel. Men, hur definerar jag samma funktion i CS? Jo då, så här:

1
2
3
4
5
square = ( x ) ->
    x * x
 
cube = ( x ) ->
    square( x ) * x

Ganska simpelt va? Sparar en del onödiga tecken i källkoden, även om den automatiskt kommer kompileras till vanliga javascript för använding.

Default värden för funktioner har man tidigare behövt skriva på de sättet att man först kollar om det variablen inte är null, om den är det så sätter man ett default värde, i CS är det enklare och fungerar så här:

1
2
orderDrink = ( sizeOfCup, drink = "coffe" ) ->
    "I would like a #{sizeOfCup} with #{drink}"

I princip identiskt med hur man skulle sätta ett default värde i t.ex. PHP. Notera även sättet att skriva ut variabler i strängar.

Objekt och arrayer

Objekt och arrayer fungerar likadant som vanligt i JavaScript, så det här borde inte vara allt för svårt att förstå.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Array
playlist = ["Rededication to my Ex", "Mo money mo problems"]
 
// Objekt
albums = { Lloyd : "Untitled", BIG : "Life after death" }
 
// Du beöver inte använda dig utav komma (,) när du definerar element
// i arrayer eller objekt, det går lika bra med whitespace.
numbers = [
    1, 0, 0, 1
    0, 0, 0, 1,
    1 1 1 0
]
 
// Ytterliggare objekt.
artists = 
    Tupac:
        album : 'All eyes on me'
        year : 1996
        label : 'Death Row Records'
    Game:
        album : 'The R.E.D Album'
        year : 2011
        label : 'Geffen/Aftermath'

Villkorliga tilldelningar

Det mesta i CoffeScript är verkligen lättläst, speciellt if..else-satser.

1
2
3
4
5
6
mood = topOfTheWorld if finished
 
if finished and topOfTheWorld
   goBuyABeer()
else
   tryAgain()

Det måste vara en av de kortaste if satserna jag sett! Nedan följer motsvarigheten i ren JavaScript. Skillnaden i läsbarheten är verkligen enorm.

1
2
3
4
5
6
7
8
9
10
11
12
var mood, topOfTheWorld, finished;
 
if ( finished ) {
    mood = topOfTheWorld;
}
 
if ( finished && topOfTheWorld ) {
    goBuyABeer();
}
else {
    tryAgain()
}

Loopar

Vi alla vet hur jobbiga, i vissa fall, utmannande For-loopar kan vara, men vet ni vad? De blir också enklare i CoffeScript.

1
2
3
4
5
6
7
8
// Loopa igenom food och skicka till eat() för varje element i 
 
arrayen. 
eat food for food in ['cerial', 'cheese', 'wine']
 
// Loopa igenom måltider och plugga på i, skicka till menu()
courses = ['greens', 'caviar', 'truffles', 'roast']
menu i + 1, dish for dish, i in courses

Operatorer och alias

För att läsa mer om de olika operationerna och aliasen som finns i CoffeScript, besök den här sidan.

Slutsats

Nu har jag inte gått in på djupet och undersökt exakt hur CS fungerar, men jag hoppas i alla fall att ni har fått en hum om vad det går ut på och hur enkelt och lättläst det är. Nu har jag endast skrapat lite på ytan och inte kikat på några större applikationer skrivna i CS men det är därför jag har skrivit den här artikeln, för att bilda mig en grundläggande förståelse över syntaxen och hur den användas. Nu ska denna lärdom appliceras genom att Todo SpineJS av Addy Osmani ska undersökas, och jämföras med samma applikation i Backbone.

Källa: coffescript.org

steg 04 – fördjupning

Då var man framme vid fördjupningen, och jag har både funderat på att fördjupa mig inom NodeJS eller en jämförelse mellan Backbone och SpineJS. Jag har haft annat för mig, tack vare det fina vädret här i Norrköping så det har inte blivit så mycket gjort men jag ska ta tag i det nu idag eller till helgen och självklart göra klart det med.

Försökte få upp en NodeJS server på min windows maskin men tyvärr så får jag bara fram ‘Undefined’ hur jag än försöker så kanske har jag gjort någonting fel? Det återstår att se men ska försöka fördjupa mig inom Addy Osmanis TodoMVC. Då specifik inriktat på skillnaden mellan Todo applikationen i Backbone och Spine.