SlideShare a Scribd company logo
1 of 116
Groovy: Efficiency Oriented Programming
Lecture 12
Master Proteomics & Bioinformatics - University of Geneva
Alexandre Masselot - summer 2011
Agenda

‣ Linux tip-of-the-day
‣ GORM relationships
‣ Database batches
‣ Application configuration
Linux tip-of-the-day: bash environment variables

‣ Set a variable to be read by further terminal process
 export GRAILS_HOME=$HOME/local/grails-1.3.7
Linux tip-of-the-day: bash environment variables

‣ Set a variable to be read by further terminal process
 export GRAILS_HOME=$HOME/local/grails-1.3.7

‣ Prepend the path to give priority:
 export PATH=$GRAILS_HOME/bin:$PATH
Linux tip-of-the-day: bash environment variables

‣ Set a variable to be read by further terminal process
 export GRAILS_HOME=$HOME/local/grails-1.3.7

‣ Prepend the path to give priority:
 export PATH=$GRAILS_HOME/bin:$PATH

‣ Variables are not shared among “parallel” consoles
Linux tip-of-the-day: bash environment variables

‣ Set a variable to be read by further terminal process
 export GRAILS_HOME=$HOME/local/grails-1.3.7

‣ Prepend the path to give priority:
 export PATH=$GRAILS_HOME/bin:$PATH

‣ Variables are not shared among “parallel” consoles
‣ To set variable a bash startup, edit either
 ~/.bashrc
 ~/.profile
Objects dependancies ↔ domain relationships
        (↔ foreign keys constraints)
Different type of relationships
Different type of relationships

‣ one-to-one
Different type of relationships

‣ one-to-one
‣ one-to-many
Different type of relationships

‣ one-to-one
‣ one-to-many
‣ many-to-many
The pedestrian one-to-many

‣Message.groovy
Person commiter
The pedestrian one-to-many

‣Message.groovy
Person commiter

‣ Person.groovy
Set<Messages> messages=[]
def addToMessages(msg){
  msg.commiter=this
  msg.save()
  messages.add(msg)
}
The pedestrian one-to-many

‣Message.groovy
Person commiter

‣ Person.groovy
Set<Messages> messages=[]
def addToMessages(msg){
  msg.commiter=this
  msg.save()
  messages.add(msg)
}

‣ And we do not mention
 - added messages already has a commiter member
 - cascade deletion
GORM: do not care about SQL/Hibernate
Dependency to a single bean: typed member
Dependency to a list of beans: hasMany
Dependency direction: belongsTo
One-to-one relationship
Example: a Profile object with details for a user
        1 Person ↔ 0:1 Profile
one-to-one   (cont’d)
one-to-one                              (cont’d)

class Profile {

 String homePage


    static constraints = {
      homePage(url:true, blank:false)
    }
}


class Person {
  ...
  Profile profile
  static constraints = {
    ...
    profile(nullable:true)
  }
}
one-to-one                            (cont’d)

class Profile {

 String homePage


 static belongsTo = Person
  static constraints = {
    homePage(url:true, blank:false)
  }
}


class Person {
  ...
  Profile profile
  static constraints = {
    ...
    profile(nullable:true)
  }
}
one-to-one                                      (cont’d)




 ‣ Setting a profile
  joe.profile=new Profile(url=‘http://www.example.com’)
  joe.save()
one-to-one                                      (cont’d)




 ‣ Setting a profile
  joe.profile=new Profile(url=‘http://www.example.com’)
  joe.save()

 ‣ Deleting cascades:
  joe.delete()
one-to-one                          (end)


 void testProfile(){

 
   assert Person.count() == 0

 
   assert Profile.count() == 0





 





 }
one-to-one                                                      (end)


 void testProfile(){

 
   assert Person.count() == 0

 
   assert Profile.count() == 0

 
   Person p=new Person(firstName:'Lucky', lastName:'Luke',
                          email:'lucky.luke@morris.com',
                          username:'lucky_luke'
                          ).save(failOnError:true)

 
   assert Person.count() == 1

 
   assert Profile.count() == 0





 





 }
one-to-one                                                        (end)


 void testProfile(){

 
   assert Person.count() == 0

 
   assert Profile.count() == 0

 
     Person p=new Person(firstName:'Lucky', lastName:'Luke',
                            email:'lucky.luke@morris.com',
                            username:'lucky_luke'
                            ).save(failOnError:true)

 
     assert Person.count() == 1

 
     assert Profile.count() == 0


   
   p.profile = new Profile(homePage:'http://www.morris.com/~lucky-luke')

   
   p.save(failOnError:true)

   
   assert Person.count() == 1

   
   assert Profile.count() == 1

   





 }
one-to-one                                                        (end)


 void testProfile(){

 
   assert Person.count() == 0

 
   assert Profile.count() == 0

 
     Person p=new Person(firstName:'Lucky', lastName:'Luke',
                            email:'lucky.luke@morris.com',
                            username:'lucky_luke'
                            ).save(failOnError:true)

 
     assert Person.count() == 1

 
     assert Profile.count() == 0


   
   p.profile = new Profile(homePage:'http://www.morris.com/~lucky-luke')

   
   p.save(failOnError:true)

   
   assert Person.count() == 1

   
   assert Profile.count() == 1

   

   
   p.delete()

   
   assert Person.count() == 0

   
   assert Profile.count() == 0


 }
One-to-many relationship
Example
1 Person ↔ n Message
one-to-many

‣ Person.groovy
  static hasMany = [messages: Message]
one-to-many

‣ Person.groovy
  static hasMany = [messages: Message]

‣ Message.groovy

 static belongsTo = [commiter:Person]
one-to-many

‣ Person.groovy
  static hasMany = [messages: Message]

‣ Message.groovy

 static belongsTo = [commiter:Person]

‣ Add one message
joe.addToMessages(new Message(...)).save()
one-to-many

‣ Person.groovy
   static hasMany = [messages: Message]

‣ Message.groovy

 static belongsTo = [commiter:Person]

‣ Add one message
joe.addToMessages(new Message(...)).save()

‣ Delete on message
joe.removeFromMessages(my_msg).save()
one-to-many

‣ Person.groovy
   static hasMany = [messages: Message]

‣ Message.groovy
 
 static belongsTo = [commiter:Person]

‣ Add one message
 joe.addToMessages(new Message(...)).save()

‣ Delete on message
 joe.removeFromMessages(my_msg).save()

‣ Delete person + all messages
 joe.delete()
many-to-many relationships
Example: a Message contains many Tag
           and vice-versa
       n Message ↔ m Tag
many-to-many

‣ Message.groovy:
static hasMany = [tags:Tag]
many-to-many

‣ Message.groovy:
static hasMany = [tags:Tag]

‣ Tag.groovy:
class Tag{
  String name


    static hasMany = [messages:Message]
    constraints = { name(unique:true) }
}
many-to-many

‣ Message.groovy:
static hasMany = [tags:Tag]

‣ Tag.groovy:
class Tag{
  String name

    belongsTo = Message
    static hasMany = [messages:Message]
    constraints = { name(unique:true) }
}
belongsTo: who adds who?
my_msg.addToTags(my_tag).save()
Self referencing domain
Twitter example: a Person is following others
static hasMany = [
            messages: Message,
            followings: Person
                   ]
Application configuration: beyond default
Three environments:
development (run-app)
test (test-app -integration)
production (war deployment)
Goal: a persistent development database
DataSource.groovy

‣ All database configuration is stored under
 grails-app/conf/DataSource.groovy
DataSource.groovy

‣ All database configuration is stored under
 grails-app/conf/DataSource.groovy

‣ Global vs environment configuration
 dataSource {
 
 pooled = true
 
 driverClassName = "org.hsqldb.jdbcDriver"
 
 username = "sa"
 
 password = ""
 }
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"
                    "update"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"
                    "update"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
hsqldb on file

‣ Database is saved in 3 flat files
hsqldb on file

‣ Database is saved in 3 flat files
‣ e.g. devDb.script
 CREATE SCHEMA PUBLIC AUTHORIZATION DBA
 CREATE MEMORY TABLE MESSAGE(ID BIGINT GENERATED ...
 CREATE MEMORY TABLE PERSON(ID BIGINT GENERATED ...
 ALTER TABLE MESSAGE ADD CONSTRAINT FK38EB0007D6D0B12 FOREIGN
 KEY(COMMITER_ID) REFERENCES PERSON(ID)
 ALTER TABLE MESSAGE ALTER COLUMN ID RESTART WITH 3
 ALTER TABLE PERSON ALTER COLUMN ID RESTART WITH 2
 CREATE USER SA PASSWORD ""
 GRANT DBA TO SA
 SET WRITE_DELAY 10
 SET SCHEMA PUBLIC
 INSERT INTO MESSAGE VALUES(1,1,'I''m coming',1)
 INSERT INTO MESSAGE VALUES(2,0,'on Jolly Jumper',1)
 INSERT INTO PERSON VALUES(1,0,'Luke','lucky_luke',
 'lucky.luke@morris.com','2010-05-17 01:18:16.607000000','Lucky')
More configuration

‣ All configuration files reside into grails-app/conf
More configuration

‣ All configuration files reside into grails-app/conf
‣ Config.groovy handles much
More configuration

‣ All configuration files reside into grails-app/conf
‣ Config.groovy handles much
‣ For example, to set failOnError:true by default on
  all .save() calls
grails.gorm.save.failOnError = true
Populating the database with batch
Inserting elements at application start:
        BootStrap.groovy
grails-app/conf/BootStrap.groovy


class BootStrap {


 def init = { servletContext ->





   }

   def destroy = {

   }
}
grails-app/conf/BootStrap.groovy


class BootStrap {


 def init = { servletContext ->

   
   environments {

   
   
   test {

   
   
   }

   
   
   development {





   
   
   }

   
   
   production {

   
   
   }

   
   }

   }

   def destroy = {

   }
}
grails-app/conf/BootStrap.groovy
import eop.lec12.twitter.Person
import eop.lec12.twitter.Profile
class BootStrap {


 def init = { servletContext ->

   
   environments {

   
   
   test {

   
   
   }

   
   
   development {

   
   
    
   if(!Person.findByUsername('sci_am')){

   
   
    
   
   def p=new Person(firstName:'Scientific', lastName:'American',
                                      email:'info@sciam.com', username:'sci_am')

   
   
    
   
   p.save()

   
   
    
   
   p.profile=new Profile(homePage:'http://www.sciam.com').save()

   
   
    
   }

   
   
   }

   
   
   production {

   
   
   }

   
   }

   }

   def destroy = {

   }
}
grails-app/conf/BootStrap.groovy
import eop.lec12.twitter.Person
import eop.lec12.twitter.Profile
class BootStrap {


 def init = { servletContext ->

 
    environments {

 
    
   test {

 
    
   }

 
    
   development {

 
    
   
    if(!Person.findByUsername('sci_am')){

 
    
   
    
   def p=new Person(firstName:'Scientific', lastName:'American',
email:'info@sciam.com', username:'sci_am')

 
    
   
    
   p.save()

 
    
   
    
   p.profile=new Profile(homePage:'http://www.sciam.com').save()

 
    
   
    }

 
    
   }

 
    
   production {

 
    
   }

 
    }

 }

 def destroy = {

 }
}
Another batch: uploading a list of messages
Uploading a file
Uploading a file

1. a local file with a list of contents convertible into messages
  (rss feed with publication update from Scientific American & Nature)
Uploading a file

1. a local file with a list of contents convertible into messages
  (rss feed with publication update from Scientific American & Nature)

2. file is uploaded through a web page with a form
Uploading a file

1. a local file with a list of contents convertible into messages
  (rss feed with publication update from Scientific American & Nature)

2. file is uploaded through a web page with a form
3. a controller/action receive data + params
Uploading a file

1. a local file with a list of contents convertible into messages
  (rss feed with publication update from Scientific American & Nature)

2. file is uploaded through a web page with a form
3. a controller/action receive data + params
4. launch a service action to parse file & insert into database
Uploading a file

1. a local file with a list of contents convertible into messages
  (rss feed with publication update from Scientific American & Nature)

2. file is uploaded through a web page with a form
3. a controller/action receive data + params
4. launch a service action to parse file & insert into database
Input file
Input file

‣ Download Scientific American’s feed
http://rss.sciam.com/ScientificAmerican-Global
Input file

‣ Download Scientific American’s feed
 http://rss.sciam.com/ScientificAmerican-Global

‣ Nature’s feed
 http://feeds.nature.com/nature/rss/current?format=xml
Input file

‣ Download Scientific American’s feed
 http://rss.sciam.com/ScientificAmerican-Global

‣ Nature’s feed
 http://feeds.nature.com/nature/rss/current?format=xml

‣ Stored locally in
 test/data/
Input file

‣ Download Scientific American’s feed
 http://rss.sciam.com/ScientificAmerican-Global

‣ Nature’s feed
 http://feeds.nature.com/nature/rss/current?format=xml

‣ Stored locally in
 test/data/

‣ Two loader classes (with tests) to convert rss item into
  temporary MessageToLoad class
 src/groovy
http://localhost:8080/eop.lec12.twitter/message/loadList

grails-app/controllers/MessageController.groovy :
                                      loadList = {...}
grails-app/views/message/loadList.gsp
loadList.gsp

enable the form
 to upload file

   <g:form controller="message" action="loadList"
     method="post”
     enctype="multipart/form-data">
       <input type="file" name="messagefile"/>
       <g:select name="type"
                       from="${['sciam', 'nature'] }"/>
       <input type="submit"/>
   </g:form>


                       form field
                      with file data
MessageController.groovy


 def loadList = {

 





 }
MessageController.groovy


 def loadList = {

 
 if(!params.type){

 
 
 // the controller is called without parameter
        // thus we just display the form

 
 
 return

 
 }





 }
MessageController.groovy


   def loadList = {

   
 if(!params.type){

   
 
 // the controller is called without parameter
          // thus we just display the form

   
 
 return

   
 }

   
 def loaders=[

   
                sciam:new ScientificAmericanMessageLoader(),

   
                nature:new NatureMessageLoader()

   
              ]

   

   
 def loader=loaders[params.type]





 }
MessageController.groovy


   def loadList = {

   
 if(!params.type){

   
 
 // the controller is called without parameter
          // thus we just display the form

   
 
 return

   
 }

   
 def loaders=[

   
                sciam:new ScientificAmericanMessageLoader(),

   
                nature:new NatureMessageLoader()

   
              ]

   

   
 def loader=loaders[params.type]

 
    def input=request.getFile("messagefile").inputStream





 }
MessageController.groovy


   def loadList = {

   
 if(!params.type){

   
 
 // the controller is called without parameter
          // thus we just display the form

   
 
 return

   
 }

   
 def loaders=[

   
                sciam:new ScientificAmericanMessageLoader(),

   
                nature:new NatureMessageLoader()

   
              ]

   

   
 def loader=loaders[params.type]

 
    def input=request.getFile("messagefile").inputStream

 
    def n=messageService.loadMessages(loader, input)



 }
MessageController.groovy


   def loadList = {

   
 if(!params.type){

   
 
 // the controller is called without parameter
          // thus we just display the form

   
 
 return

   
 }

   
 def loaders=[

   
                sciam:new ScientificAmericanMessageLoader(),

   
                nature:new NatureMessageLoader()

   
              ]

   

   
 def loader=loaders[params.type]

 
    def input=request.getFile("messagefile").inputStream

 
    def n=messageService.loadMessages(loader, input)

 
    flash.uploadedMessage=
              "file   was correctly uploaded with $n entries"

 }
MessageService

‣ generate grails-app/services/MessageService.groovy
create-service message
MessageService

‣ generate grails-app/services/MessageService.groovy
 create-service message

‣ In MessageController.groovy, service is referred simply with
 def messageService
MessageService

‣ generate grails-app/services/MessageService.groovy
 create-service message

‣ In MessageController.groovy, service is referred simply with
 def messageService

‣ See project source for actual bean insertion
Extracting a bean list
All messages: Message.list()
Exact Match
retList=Message.findAllByCommiter(
                      Person.findByUsername(params.username)
                                 )
Text with wildcard: %
retList=Message.findAllByTextLike("%$params.text%")
Match with comparator
retList=Message.findAllByDateGreaterThan(params.date)
Multiple constraints
retList=Message.findAllByTextLikeAndDateGreaterThan(
                             "%$params.text%", params.date
                                                    )
Pagination
retList=Message.findAllByTextLike("%$params.text%",
               [max:10, offset:19, sort:‘date’, order:‘desc’]
                                 )
More power:
Hibernate criteria & HQL
Application configuration: beyond default
Three environments:
development (run-app)
test (test-app -integration)
production (war deployment)
Goal: a persistent development database
DataSource.groovy

‣ All database configuration is stored under
 grails-app/conf/DataSource.groovy
DataSource.groovy

‣ All database configuration is stored under
 grails-app/conf/DataSource.groovy

‣ Global vs environment configuration
 dataSource {
 
 pooled = true
 
 driverClassName = "org.hsqldb.jdbcDriver"
 
 username = "sa"
 
 password = ""
 }
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"
                    "update"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
DataSource.groovy
                         (cont’d)

environments {

 development {

 
 dataSource {
         // one of 'create', 'create-drop','update'

 
 
 dbCreate = "create-drop"
                    "update"

 
 
 url = "jdbc:hsqldb:mem:devDB"
               "jdbc:hsqldb:file:devDb:devDB"

 
 }

 }

 test {
     //skipped

 }

 production {

 
 dataSource {

 
 
 dbCreate = "update"

 
 
 url = "jdbc:hsqldb:file:prodDb;shutdown=true"

 
 }

 }
}
hsqldb on file

‣ Database is saved in 3 flat files
hsqldb on file

‣ Database is saved in 3 flat files
‣ e.g. devDb.script
 CREATE SCHEMA PUBLIC AUTHORIZATION DBA
 CREATE MEMORY TABLE MESSAGE(ID BIGINT GENERATED ...
 CREATE MEMORY TABLE PERSON(ID BIGINT GENERATED ...
 ALTER TABLE MESSAGE ADD CONSTRAINT FK38EB0007D6D0B12 FOREIGN
 KEY(COMMITER_ID) REFERENCES PERSON(ID)
 ALTER TABLE MESSAGE ALTER COLUMN ID RESTART WITH 3
 ALTER TABLE PERSON ALTER COLUMN ID RESTART WITH 2
 CREATE USER SA PASSWORD ""
 GRANT DBA TO SA
 SET WRITE_DELAY 10
 SET SCHEMA PUBLIC
 INSERT INTO MESSAGE VALUES(1,1,'I''m coming',1)
 INSERT INTO MESSAGE VALUES(2,0,'on Jolly Jumper',1)
 INSERT INTO PERSON VALUES(1,0,'Luke','lucky_luke',
 'lucky.luke@morris.com','2010-05-17 01:18:16.607000000','Lucky')
More configuration

‣ All configuration files reside into grails-app/conf
More configuration

‣ All configuration files reside into grails-app/conf
‣ Config.groovy handles much
More configuration

‣ All configuration files reside into grails-app/conf
‣ Config.groovy handles much
‣ For example, to set failOnError:true by default on
  all .save() calls
grails.gorm.save.failOnError = true

More Related Content

What's hot

GoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDGoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDBartłomiej Kiełbasa
 
Writing Swift code with great testability
Writing Swift code with great testabilityWriting Swift code with great testability
Writing Swift code with great testabilityJohn Sundell
 
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRaimonds Simanovskis
 
Introduction to the new official C# Driver developed by 10gen
Introduction to the new official C# Driver developed by 10genIntroduction to the new official C# Driver developed by 10gen
Introduction to the new official C# Driver developed by 10genMongoDB
 
Lean React - Patterns for High Performance [ploneconf2017]
Lean React - Patterns for High Performance [ploneconf2017]Lean React - Patterns for High Performance [ploneconf2017]
Lean React - Patterns for High Performance [ploneconf2017]Devon Bernard
 
Redis for the Everyday Developer
Redis for the Everyday DeveloperRedis for the Everyday Developer
Redis for the Everyday DeveloperRoss Tuck
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
History of jQuery
History of jQueryHistory of jQuery
History of jQueryjeresig
 
The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.Workhorse Computing
 
The report of JavaOne2011 about groovy
The report of JavaOne2011 about groovyThe report of JavaOne2011 about groovy
The report of JavaOne2011 about groovyYasuharu Nakano
 
How to Design a Great API (using flask) [ploneconf2017]
How to Design a Great API (using flask) [ploneconf2017]How to Design a Great API (using flask) [ploneconf2017]
How to Design a Great API (using flask) [ploneconf2017]Devon Bernard
 
Asynchonicity: concurrency. A tale of
Asynchonicity: concurrency. A tale ofAsynchonicity: concurrency. A tale of
Asynchonicity: concurrency. A tale ofJoel Lord
 
Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Workhorse Computing
 
The promise of asynchronous PHP
The promise of asynchronous PHPThe promise of asynchronous PHP
The promise of asynchronous PHPWim Godden
 
Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with JasmineLeon van der Grient
 
Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Naresha K
 
Asynchronicity: concurrency. A tale of
Asynchronicity: concurrency. A tale ofAsynchronicity: concurrency. A tale of
Asynchronicity: concurrency. A tale ofJoel Lord
 

What's hot (20)

GoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDDGoCracow #5 Bartlomiej klimczak - GoBDD
GoCracow #5 Bartlomiej klimczak - GoBDD
 
Writing Swift code with great testability
Writing Swift code with great testabilityWriting Swift code with great testability
Writing Swift code with great testability
 
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
 
Short Introduction To "perl -d"
Short Introduction To "perl -d"Short Introduction To "perl -d"
Short Introduction To "perl -d"
 
Introduction to the new official C# Driver developed by 10gen
Introduction to the new official C# Driver developed by 10genIntroduction to the new official C# Driver developed by 10gen
Introduction to the new official C# Driver developed by 10gen
 
Lean React - Patterns for High Performance [ploneconf2017]
Lean React - Patterns for High Performance [ploneconf2017]Lean React - Patterns for High Performance [ploneconf2017]
Lean React - Patterns for High Performance [ploneconf2017]
 
Redis for the Everyday Developer
Redis for the Everyday DeveloperRedis for the Everyday Developer
Redis for the Everyday Developer
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
Rails on Oracle 2011
Rails on Oracle 2011Rails on Oracle 2011
Rails on Oracle 2011
 
History of jQuery
History of jQueryHistory of jQuery
History of jQuery
 
The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.
 
The report of JavaOne2011 about groovy
The report of JavaOne2011 about groovyThe report of JavaOne2011 about groovy
The report of JavaOne2011 about groovy
 
How to Design a Great API (using flask) [ploneconf2017]
How to Design a Great API (using flask) [ploneconf2017]How to Design a Great API (using flask) [ploneconf2017]
How to Design a Great API (using flask) [ploneconf2017]
 
Asynchonicity: concurrency. A tale of
Asynchonicity: concurrency. A tale ofAsynchonicity: concurrency. A tale of
Asynchonicity: concurrency. A tale of
 
Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.
 
The promise of asynchronous PHP
The promise of asynchronous PHPThe promise of asynchronous PHP
The promise of asynchronous PHP
 
Testing Backbone applications with Jasmine
Testing Backbone applications with JasmineTesting Backbone applications with Jasmine
Testing Backbone applications with Jasmine
 
Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015Pragmatic Browser Automation with Geb - GIDS 2015
Pragmatic Browser Automation with Geb - GIDS 2015
 
Little Big Ruby
Little Big RubyLittle Big Ruby
Little Big Ruby
 
Asynchronicity: concurrency. A tale of
Asynchronicity: concurrency. A tale ofAsynchronicity: concurrency. A tale of
Asynchronicity: concurrency. A tale of
 

Viewers also liked

Viewers also liked (6)

groovy & grails - lecture 5
groovy & grails - lecture 5groovy & grails - lecture 5
groovy & grails - lecture 5
 
groovy & grails - lecture 3
groovy & grails - lecture 3groovy & grails - lecture 3
groovy & grails - lecture 3
 
groovy & grails - lecture 10
groovy & grails - lecture 10groovy & grails - lecture 10
groovy & grails - lecture 10
 
groovy & grails - lecture 9
groovy & grails - lecture 9groovy & grails - lecture 9
groovy & grails - lecture 9
 
groovy & grails - lecture 6
groovy & grails - lecture 6groovy & grails - lecture 6
groovy & grails - lecture 6
 
groovy & grails - lecture 1
groovy & grails - lecture 1groovy & grails - lecture 1
groovy & grails - lecture 1
 

Similar to groovy & grails - lecture 12

Unit testing powershell
Unit testing powershellUnit testing powershell
Unit testing powershellMatt Wrock
 
Flask patterns
Flask patternsFlask patterns
Flask patternsit-people
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitZachary Klein
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Leonardo Soto
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB jhchabran
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Leonardo Soto
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Codestasimus
 
The promise of asynchronous php
The promise of asynchronous phpThe promise of asynchronous php
The promise of asynchronous phpWim Godden
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integraçãoVinícius Pretto da Silva
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesLindsay Holmwood
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...DynamicInfraDays
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessorAlessandro Nadalin
 
CouchDB : More Couch
CouchDB : More CouchCouchDB : More Couch
CouchDB : More Couchdelagoya
 
Couchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problemCouchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problemdelagoya
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data ObjectsWez Furlong
 

Similar to groovy & grails - lecture 12 (20)

Unit testing powershell
Unit testing powershellUnit testing powershell
Unit testing powershell
 
Everyday's JS
Everyday's JSEveryday's JS
Everyday's JS
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to Orbit
 
React 101
React 101React 101
React 101
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
 
The promise of asynchronous php
The promise of asynchronous phpThe promise of asynchronous php
The promise of asynchronous php
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integração
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessor
 
CouchDB : More Couch
CouchDB : More CouchCouchDB : More Couch
CouchDB : More Couch
 
Couchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problemCouchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problem
 
JavaScript Neednt Hurt - JavaBin talk
JavaScript Neednt Hurt - JavaBin talkJavaScript Neednt Hurt - JavaBin talk
JavaScript Neednt Hurt - JavaBin talk
 
Presentation1
Presentation1Presentation1
Presentation1
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data Objects
 

Recently uploaded

Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 

Recently uploaded (20)

Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 

groovy & grails - lecture 12

  • 1. Groovy: Efficiency Oriented Programming Lecture 12 Master Proteomics & Bioinformatics - University of Geneva Alexandre Masselot - summer 2011
  • 2. Agenda ‣ Linux tip-of-the-day ‣ GORM relationships ‣ Database batches ‣ Application configuration
  • 3. Linux tip-of-the-day: bash environment variables ‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7
  • 4. Linux tip-of-the-day: bash environment variables ‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7 ‣ Prepend the path to give priority: export PATH=$GRAILS_HOME/bin:$PATH
  • 5. Linux tip-of-the-day: bash environment variables ‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7 ‣ Prepend the path to give priority: export PATH=$GRAILS_HOME/bin:$PATH ‣ Variables are not shared among “parallel” consoles
  • 6. Linux tip-of-the-day: bash environment variables ‣ Set a variable to be read by further terminal process export GRAILS_HOME=$HOME/local/grails-1.3.7 ‣ Prepend the path to give priority: export PATH=$GRAILS_HOME/bin:$PATH ‣ Variables are not shared among “parallel” consoles ‣ To set variable a bash startup, edit either ~/.bashrc ~/.profile
  • 7. Objects dependancies ↔ domain relationships (↔ foreign keys constraints)
  • 8. Different type of relationships
  • 9. Different type of relationships ‣ one-to-one
  • 10. Different type of relationships ‣ one-to-one ‣ one-to-many
  • 11. Different type of relationships ‣ one-to-one ‣ one-to-many ‣ many-to-many
  • 13. The pedestrian one-to-many ‣Message.groovy Person commiter ‣ Person.groovy Set<Messages> messages=[] def addToMessages(msg){ msg.commiter=this msg.save() messages.add(msg) }
  • 14. The pedestrian one-to-many ‣Message.groovy Person commiter ‣ Person.groovy Set<Messages> messages=[] def addToMessages(msg){ msg.commiter=this msg.save() messages.add(msg) } ‣ And we do not mention - added messages already has a commiter member - cascade deletion
  • 15. GORM: do not care about SQL/Hibernate
  • 16. Dependency to a single bean: typed member
  • 17. Dependency to a list of beans: hasMany
  • 20. Example: a Profile object with details for a user 1 Person ↔ 0:1 Profile
  • 21. one-to-one (cont’d)
  • 22. one-to-one (cont’d) class Profile { String homePage static constraints = { homePage(url:true, blank:false) } } class Person { ... Profile profile static constraints = { ... profile(nullable:true) } }
  • 23. one-to-one (cont’d) class Profile { String homePage static belongsTo = Person static constraints = { homePage(url:true, blank:false) } } class Person { ... Profile profile static constraints = { ... profile(nullable:true) } }
  • 24. one-to-one (cont’d) ‣ Setting a profile joe.profile=new Profile(url=‘http://www.example.com’) joe.save()
  • 25. one-to-one (cont’d) ‣ Setting a profile joe.profile=new Profile(url=‘http://www.example.com’) joe.save() ‣ Deleting cascades: joe.delete()
  • 26. one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 }
  • 27. one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 Person p=new Person(firstName:'Lucky', lastName:'Luke', email:'lucky.luke@morris.com', username:'lucky_luke' ).save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 0 }
  • 28. one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 Person p=new Person(firstName:'Lucky', lastName:'Luke', email:'lucky.luke@morris.com', username:'lucky_luke' ).save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 0 p.profile = new Profile(homePage:'http://www.morris.com/~lucky-luke') p.save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 1 }
  • 29. one-to-one (end) void testProfile(){ assert Person.count() == 0 assert Profile.count() == 0 Person p=new Person(firstName:'Lucky', lastName:'Luke', email:'lucky.luke@morris.com', username:'lucky_luke' ).save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 0 p.profile = new Profile(homePage:'http://www.morris.com/~lucky-luke') p.save(failOnError:true) assert Person.count() == 1 assert Profile.count() == 1 p.delete() assert Person.count() == 0 assert Profile.count() == 0 }
  • 31. Example 1 Person ↔ n Message
  • 32. one-to-many ‣ Person.groovy static hasMany = [messages: Message]
  • 33. one-to-many ‣ Person.groovy static hasMany = [messages: Message] ‣ Message.groovy static belongsTo = [commiter:Person]
  • 34. one-to-many ‣ Person.groovy static hasMany = [messages: Message] ‣ Message.groovy static belongsTo = [commiter:Person] ‣ Add one message joe.addToMessages(new Message(...)).save()
  • 35. one-to-many ‣ Person.groovy static hasMany = [messages: Message] ‣ Message.groovy static belongsTo = [commiter:Person] ‣ Add one message joe.addToMessages(new Message(...)).save() ‣ Delete on message joe.removeFromMessages(my_msg).save()
  • 36. one-to-many ‣ Person.groovy static hasMany = [messages: Message] ‣ Message.groovy static belongsTo = [commiter:Person] ‣ Add one message joe.addToMessages(new Message(...)).save() ‣ Delete on message joe.removeFromMessages(my_msg).save() ‣ Delete person + all messages joe.delete()
  • 38. Example: a Message contains many Tag and vice-versa n Message ↔ m Tag
  • 40. many-to-many ‣ Message.groovy: static hasMany = [tags:Tag] ‣ Tag.groovy: class Tag{ String name static hasMany = [messages:Message] constraints = { name(unique:true) } }
  • 41. many-to-many ‣ Message.groovy: static hasMany = [tags:Tag] ‣ Tag.groovy: class Tag{ String name belongsTo = Message static hasMany = [messages:Message] constraints = { name(unique:true) } }
  • 45. Twitter example: a Person is following others
  • 46. static hasMany = [ messages: Message, followings: Person ]
  • 47.
  • 49. Three environments: development (run-app) test (test-app -integration) production (war deployment)
  • 50. Goal: a persistent development database
  • 51. DataSource.groovy ‣ All database configuration is stored under grails-app/conf/DataSource.groovy
  • 52. DataSource.groovy ‣ All database configuration is stored under grails-app/conf/DataSource.groovy ‣ Global vs environment configuration dataSource { pooled = true driverClassName = "org.hsqldb.jdbcDriver" username = "sa" password = "" }
  • 53. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 54. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 55. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 56. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 57. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 58. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 59. hsqldb on file ‣ Database is saved in 3 flat files
  • 60. hsqldb on file ‣ Database is saved in 3 flat files ‣ e.g. devDb.script CREATE SCHEMA PUBLIC AUTHORIZATION DBA CREATE MEMORY TABLE MESSAGE(ID BIGINT GENERATED ... CREATE MEMORY TABLE PERSON(ID BIGINT GENERATED ... ALTER TABLE MESSAGE ADD CONSTRAINT FK38EB0007D6D0B12 FOREIGN KEY(COMMITER_ID) REFERENCES PERSON(ID) ALTER TABLE MESSAGE ALTER COLUMN ID RESTART WITH 3 ALTER TABLE PERSON ALTER COLUMN ID RESTART WITH 2 CREATE USER SA PASSWORD "" GRANT DBA TO SA SET WRITE_DELAY 10 SET SCHEMA PUBLIC INSERT INTO MESSAGE VALUES(1,1,'I''m coming',1) INSERT INTO MESSAGE VALUES(2,0,'on Jolly Jumper',1) INSERT INTO PERSON VALUES(1,0,'Luke','lucky_luke', 'lucky.luke@morris.com','2010-05-17 01:18:16.607000000','Lucky')
  • 61. More configuration ‣ All configuration files reside into grails-app/conf
  • 62. More configuration ‣ All configuration files reside into grails-app/conf ‣ Config.groovy handles much
  • 63. More configuration ‣ All configuration files reside into grails-app/conf ‣ Config.groovy handles much ‣ For example, to set failOnError:true by default on all .save() calls grails.gorm.save.failOnError = true
  • 65. Inserting elements at application start: BootStrap.groovy
  • 66. grails-app/conf/BootStrap.groovy class BootStrap { def init = { servletContext -> } def destroy = { } }
  • 67. grails-app/conf/BootStrap.groovy class BootStrap { def init = { servletContext -> environments { test { } development { } production { } } } def destroy = { } }
  • 68. grails-app/conf/BootStrap.groovy import eop.lec12.twitter.Person import eop.lec12.twitter.Profile class BootStrap { def init = { servletContext -> environments { test { } development { if(!Person.findByUsername('sci_am')){ def p=new Person(firstName:'Scientific', lastName:'American', email:'info@sciam.com', username:'sci_am') p.save() p.profile=new Profile(homePage:'http://www.sciam.com').save() } } production { } } } def destroy = { } }
  • 69. grails-app/conf/BootStrap.groovy import eop.lec12.twitter.Person import eop.lec12.twitter.Profile class BootStrap { def init = { servletContext -> environments { test { } development { if(!Person.findByUsername('sci_am')){ def p=new Person(firstName:'Scientific', lastName:'American', email:'info@sciam.com', username:'sci_am') p.save() p.profile=new Profile(homePage:'http://www.sciam.com').save() } } production { } } } def destroy = { } }
  • 70. Another batch: uploading a list of messages
  • 72. Uploading a file 1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature)
  • 73. Uploading a file 1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature) 2. file is uploaded through a web page with a form
  • 74. Uploading a file 1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature) 2. file is uploaded through a web page with a form 3. a controller/action receive data + params
  • 75. Uploading a file 1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature) 2. file is uploaded through a web page with a form 3. a controller/action receive data + params 4. launch a service action to parse file & insert into database
  • 76. Uploading a file 1. a local file with a list of contents convertible into messages (rss feed with publication update from Scientific American & Nature) 2. file is uploaded through a web page with a form 3. a controller/action receive data + params 4. launch a service action to parse file & insert into database
  • 78. Input file ‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global
  • 79. Input file ‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global ‣ Nature’s feed http://feeds.nature.com/nature/rss/current?format=xml
  • 80. Input file ‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global ‣ Nature’s feed http://feeds.nature.com/nature/rss/current?format=xml ‣ Stored locally in test/data/
  • 81. Input file ‣ Download Scientific American’s feed http://rss.sciam.com/ScientificAmerican-Global ‣ Nature’s feed http://feeds.nature.com/nature/rss/current?format=xml ‣ Stored locally in test/data/ ‣ Two loader classes (with tests) to convert rss item into temporary MessageToLoad class src/groovy
  • 83. loadList.gsp enable the form to upload file <g:form controller="message" action="loadList" method="post” enctype="multipart/form-data"> <input type="file" name="messagefile"/> <g:select name="type" from="${['sciam', 'nature'] }"/> <input type="submit"/> </g:form> form field with file data
  • 85. MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } }
  • 86. MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] }
  • 87. MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] def input=request.getFile("messagefile").inputStream }
  • 88. MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] def input=request.getFile("messagefile").inputStream def n=messageService.loadMessages(loader, input) }
  • 89. MessageController.groovy def loadList = { if(!params.type){ // the controller is called without parameter // thus we just display the form return } def loaders=[ sciam:new ScientificAmericanMessageLoader(), nature:new NatureMessageLoader() ] def loader=loaders[params.type] def input=request.getFile("messagefile").inputStream def n=messageService.loadMessages(loader, input) flash.uploadedMessage= "file was correctly uploaded with $n entries" }
  • 91. MessageService ‣ generate grails-app/services/MessageService.groovy create-service message ‣ In MessageController.groovy, service is referred simply with def messageService
  • 92. MessageService ‣ generate grails-app/services/MessageService.groovy create-service message ‣ In MessageController.groovy, service is referred simply with def messageService ‣ See project source for actual bean insertion
  • 95. Exact Match retList=Message.findAllByCommiter( Person.findByUsername(params.username) )
  • 96. Text with wildcard: % retList=Message.findAllByTextLike("%$params.text%")
  • 99. Pagination retList=Message.findAllByTextLike("%$params.text%", [max:10, offset:19, sort:‘date’, order:‘desc’] )
  • 102. Three environments: development (run-app) test (test-app -integration) production (war deployment)
  • 103. Goal: a persistent development database
  • 104. DataSource.groovy ‣ All database configuration is stored under grails-app/conf/DataSource.groovy
  • 105. DataSource.groovy ‣ All database configuration is stored under grails-app/conf/DataSource.groovy ‣ Global vs environment configuration dataSource { pooled = true driverClassName = "org.hsqldb.jdbcDriver" username = "sa" password = "" }
  • 106. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 107. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 108. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 109. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 110. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 111. DataSource.groovy (cont’d) environments { development { dataSource { // one of 'create', 'create-drop','update' dbCreate = "create-drop" "update" url = "jdbc:hsqldb:mem:devDB" "jdbc:hsqldb:file:devDb:devDB" } } test { //skipped } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 112. hsqldb on file ‣ Database is saved in 3 flat files
  • 113. hsqldb on file ‣ Database is saved in 3 flat files ‣ e.g. devDb.script CREATE SCHEMA PUBLIC AUTHORIZATION DBA CREATE MEMORY TABLE MESSAGE(ID BIGINT GENERATED ... CREATE MEMORY TABLE PERSON(ID BIGINT GENERATED ... ALTER TABLE MESSAGE ADD CONSTRAINT FK38EB0007D6D0B12 FOREIGN KEY(COMMITER_ID) REFERENCES PERSON(ID) ALTER TABLE MESSAGE ALTER COLUMN ID RESTART WITH 3 ALTER TABLE PERSON ALTER COLUMN ID RESTART WITH 2 CREATE USER SA PASSWORD "" GRANT DBA TO SA SET WRITE_DELAY 10 SET SCHEMA PUBLIC INSERT INTO MESSAGE VALUES(1,1,'I''m coming',1) INSERT INTO MESSAGE VALUES(2,0,'on Jolly Jumper',1) INSERT INTO PERSON VALUES(1,0,'Luke','lucky_luke', 'lucky.luke@morris.com','2010-05-17 01:18:16.607000000','Lucky')
  • 114. More configuration ‣ All configuration files reside into grails-app/conf
  • 115. More configuration ‣ All configuration files reside into grails-app/conf ‣ Config.groovy handles much
  • 116. More configuration ‣ All configuration files reside into grails-app/conf ‣ Config.groovy handles much ‣ For example, to set failOnError:true by default on all .save() calls grails.gorm.save.failOnError = true

Editor's Notes

  1. \n
  2. \n
  3. &amp;#x201C;terminal&amp;#x201D; shell session\nno export =&gt; variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  4. &amp;#x201C;terminal&amp;#x201D; shell session\nno export =&gt; variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  5. &amp;#x201C;terminal&amp;#x201D; shell session\nno export =&gt; variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  6. &amp;#x201C;terminal&amp;#x201D; shell session\nno export =&gt; variable is not passed to forked process\nbash is not the only shell\ncsh tcsh sh etc... with different notation variations\n
  7. relationship are the heart of the domain architectures\nthe expression of the logic of how your objet are built and depends on each others\n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. approximative....\n
  16. approximative....\n
  17. approximative....\n
  18. Grails Objet Relational Mapping\nhide SQL complexity beyond a domain description\n...well to some extent\nDomain self constraints in static constraints={}\n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. instead of belongsTo = [user:Person]\nconstraints user(nullable:true)\n
  25. instead of belongsTo = [user:Person]\nconstraints user(nullable:true)\n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. cf last time\n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. Bean list declaration is symmetrical.\nGORM offers the possibility to put a direction of dependency\n
  39. Tag only have a name, which is a unique constrained property\n
  40. belongs : directions\n
  41. belongs : directions\n
  42. belongs : directions\n
  43. \n
  44. see MessageIntegrationTests / testTags() for more tests and examples\n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. i.e. stay on disk, survive restart run-app\n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. batch= automatic =1/manual\n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. \n
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. i.e. stay on disk, survive restart run-app\n
  101. \n
  102. \n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n