Angular 2 käyttöön

Viime ajat on tullut vietettyä Angular kakkosen kanssa. Tarkoitus oli alunperin jo joulun aikoihin käyttää kunnolla aikaa tähän uuteen päivitykseen, mutta silloin tuli kaikenlaista pientä tekemistä tielle.

Ensivaikutelma on ihastus. Kirjoittelin taannoin Aureliasta, joka on myös hieno paketti, ja jossa on paljon samaa Angularin kanssa. Mutta Angularin vahvana etuna on pitkät perinteet ykkösversiosta, ja sen myötä laaja käyttäjäkunta. Uusi versio ei heitä kaikkea roskiin, mutta tuo kaikenlaista mukavaa uutta. Tässä muutama highlight poimintana:

  • Typescript – ei ole pakko käyttää sitä mutta onhan se ihanaa
  • ES6 ja moduulit – käytä mitä osia kaipaat, ei tarvitse ladata kaikkea joka projektiin
  • Komponentit – ah miten elegantti ja ketterä lähestymistapa ui kehityskeen

Angular kakkosesta on ehditty jo kirjoittamaan tiukkoja vertailuja toista kuningasta – Reactia vastaan – ja puolin ja toisin. Molemmilla lähestymistavoilla lienee jatkossakin omat faninsa.

Vastaavasti laskukäyrän puolella olisi Bower ja Grunt – tuntuu että Angular kakkosen kanssa luontevaa on yhdistää npm, Webpack, ja tarvittaessa Gulp – jos sitäkään. Keep it simple, stupid! Node vitonen on myös nopea buildihommissa – ja aiemmin taisinkin jo kehaista npm:n nykyistä windows-ystävällistä hakemistorakennettakin.

Lähitulevaisuudessa tulossa jotain katsojaystävällistä getting started-tutoriaaliakin, toki hyviä on ihan Angularin omillakin sivuilla.. Mutta jos haluaa vähän helloworldiä realistisempaan mennä…

Mainokset

Netbeans 8.0.1 ja JavaScript, Angular, Require, Grunt,…

Netbeans 8 on ollut kaikista kehutuista ominaisuuksistaan huolimatta Javascript käytössä lähinnä glorifioitu tekstieditori. Tarkoittaen, että ominaisuudet vaativat toimiakseen tietyn HTML5 tyyppisen projektin, esim. Maven tai web projektit eivät kelpaa, saati sitten freeform kansiot. Sen kanssa on pärjäilty tähän asti, lähinnä Maven-tuen ja Git-tuen hyvän integraation vuoksi.

Nyt tuli kuitenkin putkesta ulos 8.0.1 päivitys, ja siinä on pieniä parannuksia tälle kentälle. Jatkossakin on vielä varaa paljon parantaa – mutta jokainen askel eteenpäin on hyvästä.

Yksi päivityksistä on parempi tuki require-moduulihallinnalle. Se löytyy nyt myös Maven projektin asetuksista:

RequireJS kirjastot

Kuten dialogista näkee, voi halutessaan määritellä polkuja myös manuaalisesti jotta moduulit löytyvät – rasti ruudussa pitäisi kuitenkin pitkälti riittää. Kun moduulit löytyvät, monet intellisense/content assist mahdollisuudet paranevat:

Content assit for require paths

Intellisense

Tosiaan kaukana ollaan vielä täydellisyydestä, parannettavaa on vielä. Intellisense on paranemaan päin mutta esim. angular templateiden ymmärtäminen aivan alkutekijöissä: nykyisellään niistä tulee lukemattomia html5 sääntömotkotuksia mm. attribuuteista ilman arvoja tai siitä mikä elementti voi olla missäkin kohtaa. Useimmat niistä ovat vääriä hälyytyksiä, mutta niiden sekaan häviävät sitten merkityksellisetkin.

Tosiaan editoripuolella suosittu vaihtoehtohan on Webstorm, itse en siitä niin innostunut koska kaupallinen ja koska Maven projektituki edelleen ainakin omalle logiikalle kehno. Muita hyviä keveämpiä open source editoreita ovat mm. Brackets, sekä Sublime. Mukavasti grunt/mavenillä rakennetussa projektissa on kivaa se että voi käyttää mitä editoria haluaa, kunhan koodin muotoiluasetukset ovat yhtenevät. Brackets tuli blogin lukijavinkkinä, ja olen siihen tykästynyt.

Grunt, Protractor, ja Istanbul kattavuusraportit e2e-testeille

Tuli ajankohtaiseksi pohtia voiko API ja E2E testeistä saada koodikattavuusanalyysejä. Käy ilmi, että Angular-alustalla Protractor ajoista voi. Yritin ensin helppoa reittiä valmiin grunt-protractor-coverage plug-inin kanssa, mutta törmäsin toistuvasti kahteen ärsyttävään virheeseen joista ei löytynyt lisätietoa ja jotka eivät ratkeeneet. Joten löysin ratkaisun joka on vielä yksinkertaisemmista paloista koottu: Instanbul moduuli ja vähän magiaa.

Istanbulille on tehty monia Grunt-plugineita, itse päädyin käyttämään perusmallia taichi.

https://github.com/taichi/grunt-istanbul

Plugarin asennus ja lataus on ihan normikamaa. Tärkeät taskimäärittelyt ovat instrument, ja makeReport.

Instrument tähän tapaan:

    instrument: {
      files: 'app/*.js',
      options: {
        lazy: true,
        basePath: 'test/coverage/instrument/'
      }
    }

Tuossa app/*.js on kansio jossa koodit muhivat, ja test/coverage/instrument on kansio johon instrumentoidut versiot koodista kopioituvat. Tämä prosessi muuttaa koodien rakennetta rajusti joten todella syytä pitää erillisiä kopioita, ja suojata omat lähdekoodit.

Tämän magian kannalta on hyvä lisätä vielä coverageVariable muuttuja, eli lopullinen ratkaisu tässä:

    instrument: {
      files: 'app/*.js',
      options: {
        lazy: true,
        coverageVariable: '__coverage__',
        basePath: 'test/coverage/instrument/'
      }
    }

Näin kun tätä instrumentoitua koodia ajetaan, kutsut päivitetään tähän globaaliin coverage muuttujaan – jonka me voimme napata (oletusnimi on aika härpäke aikaleimoineen, siksi määrittelemme sen uudelleen).

Tämän ohella on syytä clean-taskilla putsata ensin coverage kansio, sitten copy-taskilla siirtää kaikki ei-javascript tauhkat joita e2e testaukseen tarvitaan, sisältäen esim. html, kuvatiedostot, tyylisivut, jne – tietysti valmiiksi prosessoituna e2e-käyttöön kelvollisina. Tässä esimerkki copy-taskin sisällöstä:

coverageE2E: {
 files: [
 {expand: true, cwd: './dist/lib', src: '**/*.*', dest: 'coverageE2E/lib'},
 {expand: true, cwd: './dist/', src: '**/*.html', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.png', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.jpg', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.gif', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.ico', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.svg', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.css', dest: 'coverageE2E'},
 {expand: true, cwd: './dist', src: '**/*.css', dest: 'coverageE2E'},
 {expand: true, cwd: './dist/fonts', src: '*.*', dest: 'coverageE2E/fonts'},
 {expand: true, cwd: './dist/lib', src: '*.*', dest: 'coverageE2E/lib'},
 {expand: true, cwd: './WEB-INF', src: '*.*', dest: 'coverageE2E/WEB-INF'},
 // Here, extra copy operation since istanbul puts instrumented code in a subdir to a wrong path
 {expand: true, cwd: './coverageE2E/dist', src: '**/*.*', dest: './coverageE2E'}
 ]
 }

Nyt kun asiaa oikein ääneen mietin, niin kenties tätäkin voisi yleistää, esim. kopioi kaikki ei- .js päätteiset tiedostot?

Nykyisellään Istanbul instrumentointi pakottaa kohdekansioon hakemistorakenteen missä lähdekansio on – joka ei ole aina haluttua. En löytänyt pluginista säätöjä, joten tuossa copy taskissa kopioidaan dist-kansion sisältö tasoa ylemmäs, jotta se on rakenteessa jota itse käytän myös testeihin ja tuotantoon.

Seuraavaksi tulee se dirty trick: Napataan kerätty __coverage__ muuttuja ja kirjoitellaan se tiedostoon, jahka kaikki coverage on kerätty. Tämä on aivan varmasti tehtävissä nätimminkin – mutta tässä on esimerkkinä protractor-taskin onComplete-funktio joka ajetaan sen lopuksi:

onComplete: function() {
  // Let's write code coverage analysis to a file!
  browser.driver.executeScript("return __coverage__;").then(function(val) {
  fs.writeFileSync("coverageE2E/coverage.json", JSON.stringify(val));
  });
}

Lopuksi analysoidaan coverage.json tiedosto makeReport-taskilla:

module.exports = {
 src: 'coverageE2E/coverage.json',
 options: {
 type: 'lcov',
 dir: '../../../target/e2ecoverage',
 print: 'detail'
 }
};

Ja tässä kooste koko e2e_coverage taskista:

 

 

module.exports = function(grunt) {
 grunt.registerTask('e2e_coverage', [
   'dev', // First do normal build to /dist folder
   'clean:coverageE2E', // clean away old instrumented code
   'instrument', // Instrument all javascript files to instrumented folder using Istanbul
   'copy:coverageE2E', // Copy all non-javascript resources from dist to instrumented code
   'express:coverageE2E', // Run express server using instrumented folder, not dist folder
   'protractor:coverage', // Run protractor tests using instrumented folder
   'makeReport' // Use Istanbul reporting
  ]);
};

Eli tiiviisti: Käännä, putsaa vanhat pois, instrumentoi koodi Istanbulilla, kopioi html:t, tyylisivut ja kuvat sinne sekaan, aja serveri ja protractor testit instrumentoidun koodin kansiosta, ja rakenna raportti tuotoksista, taas Istanbul-pluginilla.

Nyt – tätä olisi mukavaa analysoida Sonarissa, mutta näyttäisi että siellä on Javascript-pluginissa paikka vain yhdelle lcov tiedostolle…

Huom. myös – tuon coverage tiedon keruuvaiheen voisi tehdä elegantimminkin grunt-protractor-coverage pluginilla, löytyy täältä:

https://www.npmjs.org/package/grunt-protractor-coverage

Omissa kokeiluissa en kuitenkaan saanut tuota toimimaan, se antoi kahta eri virhettä – voi johtua käytössä olevista kirjastokombinaatioista, kansiorakenteista, tms, mutta en viitsinyt haaskata siihen enempää aikaa.

Koodin laadun hifistelyä – Maven, AntRun, SonarQube

Hetken SonarQubea käytettyäni olen suuri fani. Kun koodaustiimi saa jatkuvasti metriikkaa tekemisistään, on helpompi tehdä asiat alusta asti oikein ja välttää teknistä velkaa. Olen jo aiemmin ollut ihastunut staattisiin lähdekoodianalyysiohjelmistohin, mutta Sonar on – puutteistaankin huolimatta – paljon laajempi ja osittain helppokäyttöisempi paketti.

Tässä pari uutta havaintoa matkan varrelta:

Sonarista saa näppärästi web service rajapinnan kautta metriikkaa ulos siitä miten homma etenee – näistä voi vuorostaan jopa päräytellä näppäriä graafeja tai radiaattoreita.

Esim: http://munsonarserveri:9000/api/timemachine?resource=1&metrics=coverage

Pajauttaa aikakoneesta koodin kattavuusmetriikkaa tämännäköiseen json muotoon:

[{"cols":[{"metric":"coverage"}],"cells":[{"d":"2014-06-04T15:42:12+0300","v":[0.0]},{"d":"2014-06-05T09:21:24+0300","v":[11.9]},{"d":"2014-06-09T10:22:23+0300","v":[12.8]},{"d":"2014-06-10T08:03:49+0300","v":[12.8]},{"d":"2014-06-11T07:36:49+0300","v":[14.1]},{"d":"2014-06-11T13:59:50+0300","v":[18.3]}]}]

Tuossapa tietysti sampleja joissa aikaleima, ja testikattavuuden prosentuaalinen määrä – toivon mukaan nouseva trendi.

Samoin irtoaa esim. tech debt mittari, LOC mittari, jne, joilla pystyy seuraamaan mitä tapahtuu. Nämä vaikka Android kännykän näytölle pyörimään tai robotti motkottamaan jos ylityksiä tapahtuu.

Toinen huomio: Javascript projektissa ei alkuun code coverage mittailut toimineet, vaikka lcov kattavuustiedostoja generoimmekin. Keksin pari heikkoutta Maven projektihässäkässä Sonarin suhteen:

– Sonarista on jokin aika sitten poistettu käytöstä parametrit joilla voi kertoa mistä junit testiraportit löytyvät. Monet tutorialit viittaavat vielä niihin, mutta ne eivät siis toimi.

– Grunt rakentelee kyllä iloisesti lcov-standardin mukaisia raportteja Karma -testeistä. Mutta ne menevät ikävästi kansioon jonka nimessä on selain ja käyttöjärjestelmä – jotka voivat vaihdella, ja joissa on välilyöntejä. Toinen ongelma oli lcov tiedoston sisältö – siellä oli suhteellisia polkuja jotka eivät olleet suhteessa Maven projektiin, vaan testikonfiguraatioiden paikkaan.

Joten muutama korjaus joita tein:

Ensinksi AntRun plug-in kopsaamaan lcov tiedosto fiksumpaan ja vakioidumpaan paikkaan – ja regexp replace fiksaamaan nuo polut:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.7</version>
  <executions>
    <execution>
      <id>copy-lcov-file-to-target</id>
      <phase>test</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <skip>${skipTests}</skip>
       <target>
         <copy todir="target" flatten="true">
           <fileset dir="target">
             <include name="surefire/PhantomJS*/lcov.info" />
           </fileset>
         </copy>
         <replaceregexp byline="true"
 file="${basedir}/target/lcov.info"
 match="SF:./app/"
 replace="SF:src/main/frontend/app/" />
       </target>
      </configuration>
    </execution>
  </executions>
</plugin>

Sitten vain sonaria varten yksi property asetus Maven pom.xml tiedostoon:

<properties>
    <sonar.javascript.lcov.reportPath>target/lcov.info</sonar.javascript.lcov.reportPath>
</properties>

Ja näin alkaa tippumaan testikattavuudesta raportteja.

Protractor alkoi myös tiputtelemaan JUnit yhteensopivia raportteja kun lisäili sen konfiguraatiotiedostoon tällaista:

 onPrepare: function() {
   require('jasmine-reporters');
   jasmine.getEnv().addReporter(
     new jasmine.JUnitXmlReporter('../../../target/surefire', true, true,'e2e-TEST-'));
 },

Siinäpä nämä tällä kertaa. Sonarin aikakone – time machine – antaa mukavaa metriikkaa siitä miten tarkkailija muuntaa tarkkailtavaa – quality is in the eye of the beholder.

timemachine

 

 

 

Grunt siistimmäksi

Kun AngularJs/Yeoman projekteissa puuhailee aikansa, Grunt tiedostosta voi tulla aikamoinen. Itse näen punaista kun näen satoja rivejä pitkän projektille keskeisen tiedoston jossa on kaiken lisäksi vielä useita sisäkkäisiä lohko ja taulukkorakenteita. Se tulee ampumaan jalkaan ylläpito ja bugimielessä hyvinkin pian.

Satuin törmäämään mukavaan artikkeliin koskien Gruntin siistimistä. Ideana on purkaa Taskit omaan /tasks alikansioon ja ladata ne sieltä automaattisesti. Vielä mukavampaa on purkaa kaikkien taskien optiot tasks/options alikansioon. Tässä esim. clean optiot (tasks/options/clean.js):

module.exports = {
  publish: {
    src: ['./publish/**']
  }
};

Jep, nämä toistavat isäntädokumentin module.exports rakennetta. Itse plug-inin nimeä ei tässä ole, se ratkeaa suoraan tiedoston nimen perusteella, eli tässä on siis clean plug-inin optioita säädetty.

Tässä esimerkki task:sta nimeltä server (tasks/server.js):

module.exports = function (grunt) {
 grunt.registerTask('server', [
 'express',
 'open',
 'watch'
 ]);
};

Ja tässä lopullinen gruntfile.js päähakemistosta:

module.exports = function(grunt) {
 
  // Load Grunt options automatically from tasks/options folder
  function loadConfig(path) {
    var glob = require('glob');
    var object = {};
    var key;
    glob.sync('*', {cwd: path}).forEach(function(option) {
      key = option.replace(/\.js$/,'');
      object[key] = require(path + option);
    });
    return object;
  }

  var config = { 
    pkg: grunt.file.readJSON('package.json'),
    env: process.env
  };

  grunt.util._.extend(config, loadConfig('./tasks/options/'));
  grunt.initConfig(config);
  
  // Load Grunt tasks automatically from tasks folder 
  require('load-grunt-tasks')(grunt);
  
  grunt.loadTasks("tasks");
};

Alkuperäinen Grunt oli tässä vaiheessa jo 304 riviä pitkä, ja kasvamaan päin, ja sisäkkäisten lohkojen määrä…. ai jai jai!

Samalla on tosiaan hyvä pistää Grunt lataamaan taskit automaattisesti sensijaan että ne ladataan manuaalisesti. Huomaa tuossa yllä load-grunt-tasks moduuli, joka hoitaa homman, kunhan taskin alussa on: ’grunt-’.

Eli ristiretkellä kohden parempaa tuottavuutta ja koodin hallintaa, jälleen lisää niksejä. Kunnia tästä ei mene itselle, itse vain testatin ja toimivaksi havaitsin. Alkuperäinen artikkeli tässä:

http://www.thomasboyt.com/2013/09/01/maintainable-grunt.html

 

AngularJS Grunt livereload

Leikkiessäni AngularJS:llä ja Gruntilla tuli tarpeelliseksi pohtia nopeampaa workflowta editointiin kuin raskas Maven build joka kerta kun Javascript komennon puolipistettä viilaa. Grunt tarjoaa tähän montakin tapaa, mutta löysin artikkelin jossa oli mainittu toimivaa tavaraa.

Ensin ladataan package.json tiedostossa npm:n toimesta kaikki tarvittavat moduulit, esim. tähän tapaan:

{
 "name": "MyProject",
 "version": "0.0.0",
 "devDependencies": {
 "grunt": "latest",
 "matchdep": "latest",
 "grunt-contrib-clean": "latest",
 "grunt-contrib-watch": "latest",
 "grunt-open": "latest",
 "grunt-express": "latest",

… jne

Sitten mennään gruntfile.js puolelle, ja tehdään vähän taikuutta: ladataan kaikki grunt- alkuiset kirjastot:

require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

Ja nyt voidaan grunttailla watch, open ja express moduulien kanssa. Esim. näin:

var path = require('path')
 module.exports = function (grunt) {
 grunt.initConfig({
 express: {
 all: {
 options: {
 port: 9000,
 hostname: "0.0.0.0",
 bases: [ path.resolve('.') ],
 livereload: true
 }
 }
 },
 watch: {
 static: {
 files: ['index.html'],
 options: {
 livereload: true,
 }
 },
 js: {
 files: ['app/*.js'],
 //tasks: ['concat:js', 'uglify:js'],
 options: {
 livereload: true,
 }
 },
 css: {
 files: ['content/*.less'],
 // tasks: ['less:style'],
 options: {
 livereload: true,
 }
 }
 },
 open: {
 all: {
 path: 'http://localhost:<%= express.all.options.port%>'
 }
 }
 //... etc
 })

Ja lopuksi mukava pikku server task:

// Creates the `server` task
 grunt.registerTask('server', [
 'express',
 'open',
 'watch'
 ]);

Kaikki nipussa: nyt voi sanoa: grunt server, ja grunt potkaisee käyntiin porttiin 9000 pikku serverin joka jakelee prosessoimattomat sivut, scriptit ja muut suoraan käyttöön, avaa selaimen, ja vahtii muutoksia filuihin. Kun tiedostot muuttuvat, nimensä mukaisesti livereload lataa ne uudestaan.

Mikä on kuitenkin metkointa on ylläolevan esimerkin tasks-parametrit: muutoksen yhteydessä voidaan ajaa haluttuja prosessointeja raa’alle lähdekoodille samantien.

Hauskaa! 😉

 

Lähde kokeiluihin:

http://rhumaric.com/2013/07/renewing-the-grunt-livereload-magic/

Maven pistää AngularJS:n Grunttimaan

Viime aikoina olen viettänyt enemmänkin aikaa JavaScriptin uuden aallon kirjastojen parissa, joista nimekkäimpiä varmaan AngularJS. Toisin kuin ennen vanhaan, JavaScriptiä ei nykyisellään van kirjoiteta. Siihen liittyy minifiointia, uglifiointia, tyylisivujen prosessointia, ja se on kasvanut vaatimaan build työkaluja kuten Javakin.

Grunt on yksi AngularJS:n suosituimpia build välineitä, se on vastaava kapistus kuin Maven Javalle jotakuinkin, ja aika nerokaskin. Mutta entäpä jos haluttaisiin saada aikaan yksi buildi CI palvelimelle, jossa voi kerralla tehdä asennukset, testaukset ja muut?

Maven tukee Grunt buildin ajoa moninkin tavoin, löysin lukuisia plug-ineita siihen ja Mavenhan kykenee ajamaan maven exec ja antrun taskien avulla muutenkin mitä vain komentoriviltä. Mutta haaviin jäi itselleni lopulta Eirslett Maven plug-in joka jotakuinkin teki kaiken mitä tarvitsin. Vaihtoehtoja oli monia, yksi mikä tässä houkutti oli fiksu tapa kyetä ajamaan npm ja grunt taskit ilman globaalia asennusta, joka taas saa buildit sujumaan CI palvelimella ilman kummempia kikkailuja.

https://github.com/eirslett/frontend-maven-plugin

Muita vaihtoehtoja mitä katsoin tässä, etenkin ensimmäinen näistä, Allegro plug-in, oli lupaava:

https://github.com/allegro/grunt-maven-plugin

http://addyosmani.com/blog/making-maven-grunt/

No niin, tein siis Maven Java web app pohjan, otin siis Eirslett plug-inin käyttöön build-osiossa näin:

<plugin>
 <groupId>com.github.eirslett</groupId>
 <artifactId>frontend-maven-plugin</artifactId>
 <version>0.0.14</version>
 <configuration>
 <workingDirectory>src/main/frontend</workingDirectory>
 </configuration>
 <executions>
   <execution>
     <id>install node and npm</id>
     <goals>
       <goal>install-node-and-npm</goal>
     </goals>
     <configuration>
       <nodeVersion>v0.11.12</nodeVersion>
       <npmVersion>1.4.6</npmVersion>
     </configuration>
   </execution>
   <execution>
     <id>npm install</id>
     <goals>
       <goal>npm</goal>
     </goals>
   </execution>
   <execution>
     <id>grunt build</id>
     <goals>
       <goal>grunt</goal>
     </goals>
     <configuration>
       <arguments>${grunt.args}</arguments>
     </configuration>
   </execution> 
  </executions>
  </plugin>
</plugins>

Tuossa listassa on huomattavaa lähinnä grunt.args muuttuja, joka on asetettu ylempänä näin:

<properties>
 <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <grunt.args>debug --no-color</grunt.args>
 </properties>

… mutta sen voi myös komentoriviltä ja Netbeansistä asettaa esim.

mvn -Dgrunt.args=release clean install

Cool stuff? Jenkins rouskuttaa ihan iloisesti nyt niin Java server-side kuin AngularJS kilkkeetkin yhdessä synkassa, ajaen myös testit.