Detta är mitt tredje inlägg inom ramen för kursen RIA-utveckling med JavaScript. Artikeln behandlar Steg 2 – Hello World.
Så var det äntligen dags att börja koda. All tid denna vecka har lagts på pakethantering och relationer mellan moduler i projektet med hjälp av require.js. Jag har även tittat en del på hur man enkelt kan ladda in externa resurser till projektet och för detta har jag använt pakethanteraren Bower.
Applikationen
Jag flaggade redan i förra inlägget om att applikationen som kommer att utvecklas kommer att vara en applikation för att rapportera in studentresultat på uppgifter. Jag kommer att göra många begränsningar av applikationen, speciellt nu i början. Resultat på uppgift kommer att vara begränsade till U/3/4/5, en student kommer enbart att vara direkt knuten till en specifik kurs etc.
Enkel mockup som säkert kommer att vara utsatt för väldig förändring men den ger en bild av funktionaliteten i alla fall:
Bower
Eftersom jag valt att gå all in med molnbaserade IDEn Cloud9 så vill jag även undvika att ladda ner beroenden som Backbone, Require, Bootstrap, jasmin etc. lokalt för att sedan ladda upp dessa till editorn. Jag sprang därför på Twitters pakethanterare “Bower” som på ett riktigt smidigt sätt löser detta. Efter att installerat Bower kan man enkelt i en configurationsfil tala om vilka beroenden man har och Bower hämtar sedan ner dessa och placerar de i den katalog du specificerat. I mitt fall har jag valt /scripts/lib/.
Jag hittade en utmärkt guide för detta på tutsplus. Kort tillvägagångssätt:
Installera Bower genom terminalen i Cloud9:
npm install -g bower
Efter detta så kan man skapa en konfigurationsfil, .bowerrc, där jag talar om var jag vill att mina inladdade paket ska bo:
{ "directory": "scripts/lib", }
Nästa steg är att skapa filen component.json i vilken vi talar om vilka beroenden vår applikation har och således vilka paket som Bower ska installera:
{ "name": "Student Result App", "version": "0.0.1", "dependencies": { "jquery": null, "backbone": null, "underscore": null, "requirejs": null, "text": null, "backbone.localStorage": null, "bootstrap": null, "jasmine": null } }
Det enda jag nu behöver göra för att få med dessa paket i projektet är att köra:
bower install
Detta hämtar hem senaste versionerna (null i components.json) men det går även att specificera vilken version av paket man vill ha. Det jag inte ännu testat är att hämta andra resurser med Bower men det kommer jag säkert att få användning av.
Require.js och Backbone
Jag har valt, som många andra, att använda Require.js för modulhantering och för att hantera beroenden mellan moduler. Require.js och Backbone lirar inte ihop out of the box men det finns en AMD-kompatibel version av backbone (och underscore) som man kan använda. Jag tycker inte det vara några större problem att få ihop det hela men jag identifierade snabbt att det inte är så snyggt att behöva ladda underscore så fort man laddar Backbone, eftersom Backbone är beroende av underscore. Underscore borde man behöva ladda först när man själv kommer att nyttja det.
require(['backbone', 'underscore', 'views/StudentView', 'models/StudentModel'], function(Backbone, _ , StudentView, StudentModel) ...
Jag konsulterade David kring detta och som jag förstår det så finns det lite olika lösningar på detta. Den kanske enklaste är att skapa en egen modul som man laddar som “Backbone” som i sin tur laddar underscore och backbone tillsammans med eventuella utökningar av Backbone. Alltså:
define(['backbone_pure', 'underscore', 'localstorage'], function(Backbone) { return Backbone; });
där “backbone_pure” pekar på backbone-amd och modulen ovan döps till “backbone”.
David visade även en kanske ännu renare och snyggare lösning på problemet och det är att använda “shim” i requirejs.config.
require.config({ paths: { "jquery": "lib/jquery/jquery", "underscore": "lib/underscore/underscore", "backbone": "lib/backbone/backbone", "backbone.localStorage": "lib/backbone.localStorage/backbone.localStorage", "text": "lib/text/text" }, shim: { 'backbone': { deps: ['underscore', 'jquery'], exports: 'Backbone' }, 'underscore': { exports: '_' }, 'backbone.localStorage': ['backbone'] } });
Detta ger flera fördelar. När man nu väljer att använda backbone så kommer dess beroenden (underscore och jquery) automatiskt att laddas utan att vi explicit behöver ange det. Genom att “exports” på underscore och Backbone så knyts de globala “Backbone” och “_”-objekten till require och vi underviker globaler. Detta gör också att vi inte behöver använda de AMD-kompatibla versionerna av udnerscore och backbone.
Cloud9
Denna vecka har jag också fått lägga några timmar i Cloud9s editor och jag är både förtjust och inte. När allt flyter på så flyter det på riktigt bra. Jag gillar editorn, code completion fungerar utmärkt, riktigt smidigt med terminalen etc. Dock har jag varit oerhört frustrerad över c9 från och till. Det är inte så sällan sparningar misslyckas och man måste prova att spara om och om igen. Ibland har det inte räckt att spara igen utan jag har varit tvungen att starta om editorn innan det fungerat. Jag har främst kört c9 i chrome och två gånger har tabbarna i chrome krashat när jag suttit och editerat. Helt nöjd är jag alltså inte, men jag hoppas att det flyter på lite bättre nu när jag istället valt att testa i FF.
Avgränsningar
Jag har tyvärr inte hunnit med så mycket denna iteration som jag hade hoppats. De avgränsningar jag fått göra och skjuta upp till nästa vecka är:
- Dokumentation
- Testning
Något som jag är intresserad av att kika på men ännu inte hunnit ta tag i är en snygg “deploy-lösning”. Alltså, hur driftsätter jag? Tanken här är väl att använda require.js optimizer men jag har inte hunnit titta allt för mycket på det ännu.
Länkar:
- Körbar version av applikationen: http://leitet.github.com/Student-result-app
- Git-repro: https://github.com/Leitet/Student-result-app
Bara så att ingen annan blir förvirrad av min kommentar – mitt gnäll över saknad relation mellan student och assignent är strunt. Ett assignment är en uppgift i kursen, som är oberoende av student. En student har däremot ett result på varje assignment, så det är genom result som relationen går. My bad!
Bortsett från strulet så är det ändå riktigt häftigt att du kan ha hela utvecklingen, inklusive “terminalanvändning”, i molnet!
Apropå din yUML-datamodell; nog för att den är förenklad som du sade, men visst måste det finnas en relation mellan Student och Assignment?
Väldigt söta mockups, vilket verktyg använder du där?