Brug grunt og node.js til at compile dine LESS filer

Der findes 2 store CSS preprocesore: LESS og SASS/SCSS.

Jeg bruger LESS. Mest af alt fordi det hænger godt sammen med Visual Studio som jeg bruger til daglig i mit arbejde. Den sørger for at compile mit LESS, og alt er godt.

I går opstod så det problem, at jeg var begyndt at prøve at være keativ, og få designet et nyt design til geekhub.dk.
Problemet med det var at det eneste gode program jeg har til det (Sketch), er på Mac OS så det var oplagt at lave HTML+CSS i Mac OS også.

Fordi jeg er underlig, så istedet for at vælge nogle af de tools du kan få som bare compiler din LESS (f.eks. Koala) - så skulle jeg da bruge node.js til det. Jeg brugte det som en undskyldning for at prøve Grunt

Grunt

Grunt er en taskrunner til node. Den minder om Rake til Ruby og hvis man er lidt sadomasochistisk så MSBuild.
Den giver dig kort sagt din egen commandline promt til din app, så du kan give den kommandoer og udføre tasks. Dette ville betyde at jeg kunne gøre følgende:

grunt less

Som ville compile mine less filer til CSS i det mønster som jeg ønskede. Mit egentlige mål er sådan set at den compiler ens LESS når man ændre i en LESS fil men det kommer senere :)

Installer node

Du skal først have node.js installeret på din maskine. Den er bare at hente og installere, eller evt. bruge NVM hvis man vil være mere hardcore.

Installer af grunt

Grunt logo

Da vi nu har node, så har vi nu mulighed for at bruge NPM som kommer med node til at installere pakker til noget (tænk Gems eller NuGet pakker, eller bare plugins/libraries)

Fordi det var en Mac/Linux så bemærk sudo i starten af kommandoen, hvis du bruger en Windows, så skal du ikke skrive sudo foran noget. Du skal bare huske at starte din kommandopromt som administrator.
Start din terminal/promt og skriv følgende:

npm install -g grunt-cli

Dette beder npm som at installere grunt-cli i global miljø, som giver os mulighed for at bruge grunt kommandoen over alt.

Lav et node projekt

Derefter så skal vi vil at lave os et node projekt som man gør ved at lave en package.json fil i din mappe. Brug terminalen som skriv:

npm init

Den giver dig en guide hvor du skal udfylde nogle værdier. De er ikke så vigtige lige nu, så du kan bare hoppe igennem dem.

Dette giver dig en package.json fil, som beskriver dit projekt, så andre kan bruge det hvis de henter dit projekt ned fra internettet. (f.eks. via GIT)

Installering af LESS i Grunt

Nu skal vi have installeret Grunt i projektet, og installeret alle de moduler til Grunt som gør at vi kan kompile LESS filer.

npm install grunt --save-dev
npm grunt-contrib-less --save-dev

Dette gør at vores projekt nu har grunt og less til grunt, samt at den har gemt det som udvikler dependency i package.json. Det betyder at hvis en anden person henter dine ting ned og sige ´npm install´ så vil de få disse ting installeret.

Vi skal nu lave en Gruntfile.js som beskriver vores jobs til grunt:

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    less: {
      development: {
        options: {
          paths: ["assets/css"]
        },
        files: {
          "public/assets/css/geekhub.css": "assets/css/geekhub.less"
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-less');
};

Den overstående fil er lidt stor og svær at finde rundt i. Det vige at finde ud af omkring den, er at første del (initConfig) beskriver indstillinger til LESS pakken, og siger at ved development, så skal den kigge i assets/css og compile min geekhub.less fil, og putte den i en css fil. Så skift disse stier med dine egne.

Næste del er at vi lige skal loade less tasken ind.

Dette betyder at vi nu kan sige ´grunt less´ i terminalen og den vil compile vores less, da less er en del af standard kommandoen.

Dette er sådan set alt hvad du behøver, men næste del giver lige ekstra lækkerhed med at den gør det automatisk når du ændre dine filer.

Få opsat Grunt Watch og desktop notifikationer hvis noget går galt.

Vi skal bruge 2 ekstra NPM pakker:

npm grunt-contrib-watch --save-dev npm grunt-notify --save-dev

Bemærk at disse 2 evt. kan have nogle issues med Linux eller Windows så hvis du får problemer så læs hvad du skal gøre der. Virker out of the box på Mac OS.

Derefter skal vi tilføje følgende til din ´Gruntfile´:

// Under less i initConfig
watch: {
  scripts: {
    files: ['**/*.less','*.less'],
    tasks: ['less'],
    options: {
      spawn: false,
    },
  },
},

// Under vores load task til less.
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-notify');

Hele filen skal se således ud:

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    less: {
      development: {
        options: {
          paths: ["assets/css"]
        },
        files: {
          "public/assets/css/geekhub.css": "assets/css/geekhub.less"
        }
      }
    },
    watch: {
      scripts: {
        files: ['**/*.less','*.less'],
        tasks: ['less'],
        options: {
          spawn: false,
        },
      },
    },
  });

  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-notify');

  grunt.registerTask('default', ['less']);

};

Du kan nu bare køre:

grunt watch

og den vil nu lytte efter ændringer i dine .less filer, og køre vores kommando automatisk når de ændre sig. Hvis der sker en fejl (f.eks. syntaxfejl i din LESS) så vil du få en desktop-notifikation som typisk er en informationsboks i højre top af din skærm. Smart :)

Afslutning

Jeg håber nogen kan bruge denne relativt lange guide til at få LESS compiling til at køre på Node og Grunt. Som sagt før så findes der mange lettere måde at gøre det på via apps. Men hvis du er nysgerrig eller f.eks. skal bruge dette i et build setup eller udvikler miljø, så kan denne ekstra overhead i at sætte det op sagtens være det værd.
Grunt kan utroligt mange ting, som f.eks. også at gøre din CSS mindre osv. så dette kan du frit selv bygge på.

Hvis du har nogen spørgsmål, så prøv at fange mig på twitter på @jesperbjensen

Read more

GeekHub vNext - En spade prøver at implementere CQRS

Se et, hvad der nok er en meget forkert, førsøg på at implementere CQRS i Geekhub vNext.

Read more

GeekHub vNext - Implementering af Widget featuren

Jeg har lavet en lille video, for at teste Google Hangouts. Den viser en simpel implementering af en feature til Geekhub vNext, som er implementeret i C#.

Bemærk at den er meget low-key :)

Read more

Anden dag med NodeJS - lidt noter

Så jeg fik arbejdet lidt videre på min applikation idag. Stadig ingen ide hvad den ender med, så jeg begyndte stille og roligt at lave nogle entries, og kalde det for en Journal. Pænt generisk tænkte jeg.

Jeg fik roddet med at man kunne have en user, som havde sin egen URL-root, f.eks.: awesomeapp.com/deldy Under deldy bliver der så læst nogle layout parametre ind, som gør at man kan styre layoutet. Ligenu er det kun en titel og en intro - men ideen er at man kan overstyre forskellige ting. En meget simpel form for CMS kan man kalde det, hvis man er en smule naiv omkring hvad et CMS består af :)

Dette er mine noter fra dag 2-ish:

Express Middleware

Jeg vidste at jeg ved en del actions fik brug for at loade en "bruger", og hans layout parametre. Man kunne selvfølgelig kalde en funktion i alle mine actions som ville indlæse disse værdier, men efter at Google efter en before_filter i express, så faldt svaret på middleware.

Middleware er sådan et ikke andet end hvad et HttpModule er i .NET - en simpel funktion som kører før dit rigtigt request. Her er der rig mulighed for f.eks. at loade noget data, og ligge det et sted så dine senere actions har adgang til det.

I express.js så kan man nemt lave en route, som kører på alle requests der matcher et pattern:

app.all('/:user/*', middleware.loadUser, middleware.loadLayout)

:user er mine variable, som så fanger f.eks. "deldy". Jeg har 2 stk middleware: loadUser og loadLayout. LoadUser sætte bare en user på mit request, så resten af requested kan få fat i dem. LoadLayout læser en masse key/value entries fra databasen og konvertere dem til et layout objekt, så mit view kan have adgang til den. Dette bringer mig frem til det første nye "library-of-the-day":

Underscore.js

Underscore er en hel bunke utility funktioner som f.eks. hjælper dig med at lave LINQ-agtige ting på arrays osv. De funktioner jeg brugte idag var _.map, _.object og _.extend Dette lib er også ganske brugbart i din client-side JavaScript.

Map
Map er hvad der svarer til Select i LINQ - du giver den listen, og den funktion som laver listen om til en anden liste. _.map(list, function(item) { return item + "other-stuff"});

Object
Object er en simpel måde at lave en liste af arrays (En liste med en liste af [key,value] arrays) om til et objekt. Dette blev brugt sammen med map til at lave et result fra databasen om til et objekt. Her er en lille hjælpe funktion som gør dette let. Den bruger både Map og Object:

function arrayToObject(list, keyField, valueField) {
    keyField = keyField || "key";
    valueField = valueField || "value";

    var mapped = _.map(list, function(x) {
        return [x[keyField], x[valueField]];
    });

    return _.object(mapped);
}

Extend
Extend hjælper med at lave en merge mellem to objekter. Det jeg skulle lave var at gøre det muligt at have nogle default layout parametre, og så gøre så en user kunne overskrive eller udvide de værdier som var tilgængelige. Extend hjælper med dette:

var layout = defaultLayout(user);
layout = _.extend(layout, userLayout);

Hvis layout have en key A, og userLayout også have en key A, så ville værdien for userLayout overstyre værdien fra layout - lige som vi ønsker. Nifty lille funktion, som findes i mange libs rundt omkring.

Moment.js

Den sidste ting jeg arbjedet med, var at vise en liste af entries. Selvfølgelig skulle der også vises hvornår de var oprettet. Her kommer vi over til hvordan vi formatere datoer i JavaScript. Her valgte jeg Moment.js - For at lave en javascript dato om til en string, gjorde jeg bare følgende:

return moment(date).format(format);

Mit format var så bare "DD/MM/YYYY", men den kan både parse og formatere datoer i alle mulige sprog. Et rigtigt godt bibliotek. Dette bibliotek er også rart i client-side JavaScript.

Express 'View-Helpers'

Du har rig mulighed for at lave hjælpefunktioner i Express - de kalder det bare for app.locals. Hvis du sætter app.locals, så er der adgang til disse i dit view:

app.locals({
    formatDate: function(date, format) {
        return moment(date).format(format);
    }
})

Dette giver mulighed for at kalde formatDate i dit view. Dejligt let.

Afslutning

Jeg synes det var en sjov dag idag. Jeg havde lidt mere held med mine ting idag, og jeg synes jeg fandt nogle gode og lette løsninger til nogle problemstillinger. Koden ligger på GitHub, hvis du gerne vil se hvad det er blevet til.

Read more

Første dag med NodeJS - lidt noter

Bemærk denne post ikke er en tutorial, men mere en samling af mine erfaringer

En stille og rolig søndag, gik jeg i gang med at rode med NodeJS.

Mest for at blive epic til JavaScript, men også fordi det er sjovt at lære. Jeg har kigget ganske lidt på det før, men tænkte jeg skulle give det et forsøg til.

Jeg startede med at bruge CoffeeScript, men pga. min meget lille erfaring med CoffeeScript, så fandt jeg hurtigt ud af at jeg prøvede at lære for meget på en gang. Dette var også grunden til at jeg valgte at bruge en normal database til mit projekt i stedet for at bruge MongoDB eller lign.

Databasen er så blevet PostgresSQL - mest fordi det lige passer sammen med Heroku, som nok er den platform det færdige projekt kommer ud på. Hvad er så det færdige projekt spørger du? No clue, jeg kom aldrig langt nok denne dag til at finde ud af det.

Denne blog post beskriver nogle af de ting jeg oplevede, samt nogle noter som måske kan hjælpe andre, og ikke mindst hjælpe mig i fremtiden.

Installation

Det er meget nemt at installere NodeJS, hent en installer på nettet og kør programmet.

NPM

NodeJS bruger et commandline tool til at hente "plugins" eller biblioteker med. Det hedder NPM, og skal køres via din Terminal el. cmd. Det der er vigtigt lige at forstå det er at dit projekt skal have en package.json fil, som beskriver dit projekt, og ville pakker den har behov for. Den ser nogenlunde sådan her ud:

{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "3.4.8",
    "jade": "*"
  }
}

Du kører bare denne kommando for at installere en pakke. Bemærk --save flaget som sørger for at opdatere din ´package.json´ fil:

npm install pakke --save

Express

Jeg har valgt at bruge Express som web framework. Ingen ide om det er det bedste, men det virker relativt simpelt. Du skal installere det i dit "global"-scope med NPM. Det gør at du kan bruge ´express´ programmet til at oprette din projektskabelon

npm install express -g

Derefter kører du bare denne kommando i en mappe, hvor du har dine projekter:

express projektnavn

Nu er der en mappe der hedder projektnavn hvor din applikation ligger i. Der ligger en ´app.js´ fil med din applikation. Jeg vil ikke gå mere i detaljer med express i denne post.

Postgress

Der findes et hav er data access libraries til NodeJS. Jeg valgte bare at køre på metallet, og bruge Postgres direkte med ´pg´ for ikke at have for mange lag. Ingen har taget skade er noget SQL. Jeg brugte Postgress.app til Mac OS, så det skal bare installeres, startes.
En lille note omkring at forbinde med pgAdmin til din Postgress.app database, så er ´username´ dit brugernavn til din computer, og kodeordet er blankt.
NodeJS modulet skal installeres i projektet således:

npm install pg --save

Der findes et fint codesample på deres GitHub repo. En lille note er at din connectionstring er følgende når du bruger postgress.app: ´postgres://localhost/databasenavn´

Promise Library (Q)

En af de udfordringer med NodeJS er at det meget asynkront, så jeg ledte efter et Promise library, for at gøre det lidt lettere. Jeg fandt Q. Jeg tænkte det var let nok, men jeg må indrømme at jeg nok skulle have ventet lidt med at introducere det. Q's dokumentation var lidt svær for mig at forstå. Her er lidt tips til den:

var d = Q.defer() // Laver et objekt som du kan kalde, resolve eller reject på.

d.resolve("Dit result");
d.reject("Der skete en fejl");

return d.promise(); // Laver et objekt til din kalder, som de kan kalde then og fail på.

Sådan her bruger du din defer:

myAsyncFunc().then(function(result) { console.log(result) }).fail(function(err) { console.log(err) })

Du kan også retunere noget i din ´then´, og så lave en ny then efterfølgende, hvor dens parameter så er det du retunerede i din første then.

Hvis du har en liste af defers som du vil vente på, så kan du bruge Q.all:

var calls = [myAsyncFunc(), myAsyncFunc()];
Q.all(calls).then(function(results) { console.log(results) }) // Dine resultater kommer som et array.

Afslutning

Dette var hvad jeg nåede første dag. Jeg fik skrevet lidt kode til at hente konfigurationer fra databasen i et key,value format, samt indsætte nye værdier. Alt andet tid brugte jeg på at fejle hårdt. Jeg synes NodeJS er svært at komme i gang med, især pga. alt er async, men jeg håber jeg ser lyset når jeg kommer længere. :)

Read more

Next page