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

Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesBoston Institute of Analytics
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 

Recently uploaded (20)

Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 

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