header

Grails Example Project

Simple project in Grails

Project user story

As application owner I want to deliver my application installer so the client can download it.

Acceptance Criteria

  • I need to know how many downloads I have by installer
  • Installers are Linux, Ubuntu, Mac and Windows
  • I need to know what is the client IP address

As first step, I’m going to create a project as follow:

grails create-app operating-system-downloader-stat

Next, I’m going to create a Domain as follow:

grails create-domain-class com.tim.Downloader

This domain Downloader will store download stats by operating system, it looks like this:

package com.tim

 class Downloader {
   Date dateCreated
   String address
   InstallerType type

   static constraints = {
     address blank:false,size:5..255
   }
}

About Domain

  • Grails will create an “downloader” table
  • If you define an property “dateCreated” it will set current date at creating new instances
  • Grails will validate “address” is not empty and a size between 5 and 255
  • InstallerType is an enum and I need to define it in src/groovy/ folder

Creating enum

Create path src/groovy/com/tim Create InstallerType enum as follow:

package com.tim

enum InstallerType {
 LINUX, MAC, UBUNTU, WINDOWS
}

Creating controller

In order to create a controller we need to start grails application, type this:

grails

And then:

create-controller com.tim.DownloaderController

Creating service

Services are transactional by default in Grails and is intended contains business logic, in order to create a service type:

grails

And then:

create-service com.tim.DownloaderService

Unit testing

Now we are ready to write unit test and let the test guide us to the solution, we are going to start with DownloaderControllerSpec.groovy which is located in test/unit/com/tim

package com.tim

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(DownloaderController)
class DownloaderControllerSpec extends Specification {
  DownloaderService downloaderService = Mock(DownloaderService)
  String address = "127.0.0.1"

  def setup(){
    controller.downloaderService = downloaderService
  }

  void "should count ubuntu download"() {
  when:
    controller.downloadUbuntuVersion()
  then:
    1 * downloaderService.createUbuntuStat(address)
  }
}

DownloaderControllerSpec facts

  • DownloaderService is a mock, needs to be that way since we are trying to test DownloaderController
  • The purpose “def setup()” method is execute code before any test is called
  • We assigned downloaderService to the controller in line 11
  • When we call controller.downloadUbuntuVersion() we expect to call downloaderService.createUbuntuStat() once

You can run unit tests by typing in your command line:

grails test-app :unit

Now is time to set business logic which is create a record any time someone download an ubuntu version, so let’s our DownloaderServiceSpec lead us to the light

package com.tim

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(DownloaderService)
class DownloaderControllerSpec extends Specification {

  void "should create a ubuntu download stat"() {
  when:
    def downloader = service.createUbuntuStat("127.0.0.1")
  then:
    downloader.address == "127.0.0.1"
    downloader.type == InstallerType.UBUNTU
  }
}

DownloaderServiceSpec facts

  • When we call service.createUbuntuStat(“127.0.0.1”) we are expecting that service returns an downloader object
  • Then we verify that object contains “127.0.0.1” as address and UBUNTU as InstallerType

That’s it, the first part of the story is complete, we know what is the client IP address and when an Ubuntu package is downloaded. Next step is to deliver my Ubuntu package as downloader file, let’s return to our DownloaderControllerSpec

package com.tim

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(DownloaderController)
class DownloaderControllerSpec extends Specification {
  DownloaderService downloaderService = Mock(DownloaderService)
  String address = "127.0.0.1"

  def setup(){
    controller.downloaderService = downloaderService
  }

  void "should count ubuntu download"() {
  when:
    controller.downloadUbuntuVersion()
  then:
    1 * downloaderService.createUbuntuStat(address)
    response.contentType == "application/octet-stream"
    response.getHeader("Content-disposition") =="attachment;filename=JMetadata.deb"
  }
}

DownloaderServiceSpec modifications

  • Now we are expecting that ContentType is application/octet-stream
  • The intended purpose is to be saved to disk as “arbitrary binary data”
  • We are expecting that Content-disposition is an attachment named JMetadata.deb

Now is time to see the DownloadController and DownloadService implementations

DownloaderController

package com.tim

class DownloaderController {
  def DownloaderService downloaderService

  def downloadUbuntuVersion(){
    downloaderService.createUbuntuStat(request.getRemoteAddr())

    def file = new File("/home/josdem/.jmetadata/JMetadata.deb")
    response.setContentType("application/octet-stream")
    response.setHeader("Content-disposition","attachment;filename=${file.getName()}")
    response.outputStream << file.newInputStream()
  }
}

DownloaderService

package com.tim

import grails.transaction.Transactional

@Transactional
class DownloaderService {

  def Downloader createUbuntuStat(String address){
    def downloader = new Downloader()
    downloader.address = address
    downloader.type = InstallerType.UBUNTU
    downloader.save()
  }
}

Return to the main article

comments powered by Disqus