SlideShare une entreprise Scribd logo
1  sur  78
À VOS MARQUES...
      1, 2, 3
    GRAILS!
Des applications pour le web vite, facile, et “fun”
Grails
Un “framework” pour developer des applications pour le web


             Déploiment vers JVM                  AJAX/xml/JSON
    ORM
                     i18n: internationalisation
                                Groovy                    Une fois, c’est assez
TDD/Mocks/Stubs
                                                        DRY: Don’t repeat yourself
                                   Sitemesh
       Contrôleurs
                    Inversion du contrôle/injection de dépendances
                  (IoC/DI: Inversion of control/dependency injection)

       Plugins                                       Conventions/configuration

                       GSP                                     Taglibs
Groovy


•Compilé
•Dynamique
•Exécuté sur JVM
•Lambdas et closures
•Strings dynamiques - GStrings
•Syntaxe aisée:
 - ; pas nécessaires ne sont pas requis
 - manipulations de listes
 - manipulations de “beans”/propriétés
 - méthodes sur les listes: ex: l.each {}
$   grails create-project 
>   waq-o-theque
$   cd waq-o-theque
$   ls
       application.properties
       grails-app
       lib
       scripts
       src
       test
       web-app
$ ls grails-app
    conf
    controllers
    domain
    i18n
    services
    taglib
    utils

$ grails create-domain-class 
> com.nancydeschenes.waqotheque.Bien
package com.nancydeschenes.waqotheque

class Bien {

  static constraints = {
  }

}
package com.nancydeschenes.waqotheque

class Bien {

  static constraints = {

 
 nom(blank: false, nullable:false, unique:true)

 
 description(nullable:true)
  }


 String nom

 String description

}
package com.nancydeschenes.waqotheque

class Livre extends Bien {

  static constraints = {
  }

    String auteur
}
package com.nancydeschenes.waqotheque

class CD extends Bien {

  static constraints = {

 
 interprete(nullable:true)
  }


 String interprete
}
package com.nancydeschenes.waqotheque

class DVD extends Bien {

  static constraints = {

 
 genre(nullable:true)
  }


 String genre
}
$ grails create-controller 
> com.nancydeschenes.waqotheque.Livre



     package com.nancydeschenes.waqotheque

     class LivreController {

     
 def scaffold = true;

     }
$ grails run-app
Welcome to Grails 1.3.6 - http://
grails.org/
Licensed under Apache Standard
License 2.0
Grails home is set to: /Developer/
STS-2.5.1/grails-1.3.6
[...]
Running Grails application..
Server running. Browse to http://
localhost:8080/waq-o-theque
dataSource {
    pooled = true
    driverClassName = "org.hsqldb.jdbcDriver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class =
'net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'create-
drop','update'
            url = "jdbc:hsqldb:mem:devDB"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}
dataSource {
    pooled = true
    driverClassName = "com.mysql.jdbc.Driver"
    username = "waq"
    password = "waq"
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class =
'net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "update" // one of 'create', 'create-
drop','update'
            url = "jdbc:mysql://localhost:3306/waqotheque_dev”
            show_sql = true
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:mysql://localhost:3306/waqotheque_test"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:mysql://localhost:3306/waqotheque"
        }
    }
}
sql> show tables;
-------------------------+
Tables_in_waqotheque_dev |
-------------------------+
bien                     |
-------------------------+
row in set (0.00 sec)

sql> desc bien;
------------+--------------+------+-----+---------+----------------+
Field       | Type         | Null | Key | Default | Extra          |
------------+--------------+------+-----+---------+----------------+
id          | bigint(20)   | NO   | PRI | NULL    | auto_increment |
version     | bigint(20)   | NO   |     | NULL    |                |
description | varchar(255) | YES |      | NULL    |                |
nom         | varchar(255) | NO   | UNI | NULL    |                |
class       | varchar(255) | NO   |     | NULL    |                |
genre       | varchar(255) | YES |      | NULL    |                |
interprete | varchar(255) | YES |       | NULL    |                |
auteur      | varchar(255) | YES |      | NULL    |                |
------------+--------------+------+-----+---------+----------------+
rows in set (0.00 sec)
mysql> select id, version, description from bien;
+----+---------+--------------------------------------------------+
| id | version | description                                      |
+----+---------+--------------------------------------------------+
| 1 |        0 | Un bon vieux livre...                            |
| 2 |        0 | Un roman...                                      |
| 3 |        0 | L'influence de la musique sur notre comportement |
| 4 |        0 | NULL                                             |
+----+---------+--------------------------------------------------+
4 rows in set (0.00 sec)




mysql> select id, nom, class from bien;
+----+-----------------------+-------------------------------------+
| id | nom                   | class                               |
+----+-----------------------+-------------------------------------+
| 1 | Martine ? l'?cole      | com.nancydeschenes.waqotheque.Livre |
| 2 | Au Bonheur des Ogres | com.nancydeschenes.waqotheque.Livre |
| 3 | De la note au cerveau | com.nancydeschenes.waqotheque.Livre |
| 4 | Les portes de Qu?bec | com.nancydeschenes.waqotheque.Livre |
+----+-----------------------+-------------------------------------+
4 rows in set (0.01 sec)
mysql> select id, interprete, genre, auteur from bien;
+----+-------------+---------+----------------------------------+
| id | interprete | genre    | auteur                           |
+----+-------------+---------+----------------------------------+
| 1 | NULL         | NULL    | Gilbert Delahaye, Marcel Marlier |
| 2 | NULL         | NULL    | Daniel Pennac                    |
| 3 | NULL         | NULL    | Daniel J. Levitin                |
| 4 | NULL         | NULL    | Jean-Pierre Charland             |
+----+-------------+---------+----------------------------------+
6 rows in set (0.00 sec)
SELECT * from bien;
package com.nancydeschenes.waqotheque

class CDController {

    def scaffold = true;

}


package com.nancydeschenes.waqotheque

class DVDController {


 def scaffold = true;

}
mysql> select id, version, description from bien;
+----+---------+--------------------------------------------------+
| id | version | description                                      |
+----+---------+--------------------------------------------------+
| 1 |        0 | Un bon vieux livre...                            |
| 2 |        0 | Un roman...                                      |
| 3 |        0 | L'influence de la musique sur notre comportement |
| 4 |        0 | NULL                                             |
| 5 |        0 | L'histore d'un ogre                              |
| 6 |        0 | Album of the year                                |
+----+---------+--------------------------------------------------+
6 rows in set (0.00 sec)


mysql> select id, nom, class from bien;
+----+-----------------------+-------------------------------------+
| id | nom                   | class                               |
+----+-----------------------+-------------------------------------+
| 1 | Martine ? l'?cole      | com.nancydeschenes.waqotheque.Livre |
| 2 | Au Bonheur des Ogres | com.nancydeschenes.waqotheque.Livre |
| 3 | De la note au cerveau | com.nancydeschenes.waqotheque.Livre |
| 4 | Les portes de Qu?bec | com.nancydeschenes.waqotheque.Livre |
| 5 | Shrek                  | com.nancydeschenes.waqotheque.DVD   |
| 6 | The Suburbs            | com.nancydeschenes.waqotheque.CD    |
+----+-----------------------+-------------------------------------+
6 rows in set (0.01 sec)
mysql> select id, interprete, genre, auteur from bien;
+----+-------------+---------+----------------------------------+
| id | interprete | genre    | auteur                           |
+----+-------------+---------+----------------------------------+
| 1 | NULL         | NULL    | Gilbert Delahaye, Marcel Marlier |
| 2 | NULL         | NULL    | Daniel Pennac                    |
| 3 | NULL         | NULL    | Daniel J. Levitin                |
| 4 | NULL         | NULL    | Jean-Pierre Charland             |
| 5 | NULL         | Enfants | NULL                             |
| 6 | Arcade Fire | NULL     | NULL                             |
+----+-------------+---------+----------------------------------+
6 rows in set (0.00 sec)
SELECT * from bien;
Structure de l’application Grails

grails-app/
 conf/
 controllers/
 domain/
 i18l/
 services/
 taglib/
 utils
 views
src/
 java/
 groovy/


              waq-o-theque v0.01
Sitemesh
grails-app/views/layouts/main.gsp
<!DOCTYPE html>
<html>
    <head>
        <title><g:layoutTitle default="Grails" /></title>
        <link rel="stylesheet" href="$
{resource(dir:'css',file:'main.css')}" />
        <link rel="shortcut icon" href="$
{resource(dir:'images',file:'favicon.ico')}" type="image/x-icon" /
>
        <g:layoutHead />
        <g:javascript library="application" />
    </head>



grails-app/views/index.gsp
<head>
  <title>La WAQ-o-theque</title>
  <meta name="layout" content="main" />
<body>
        <div id="spinner" class="spinner" style="display:none;">
            <img src="${resource(dir:'images',file:'spinner.gif')}"
alt="${message(code:'spinner.alt',default:'Loading...')}" />
        </div>
        <div id="grailsLogo"><a href="http://grails.org"><img
src="${resource(dir:'images',file:'grails_logo.png')}" alt="Grails"
border="0" /></a></div>
        <g:layoutBody />
    </body>
</html>


<body>
  <div id="body" style="font-size:
       30px;font-weight:bold">
      Bienvenue au site de la WAQ-o-theque!
  </div>
</body>
Style
                             CSS
web-app/css/main.css
html * {
    margin: 0;
    /*padding: 0; SELECT NOT DISPLAYED CORRECTLY IN FIREFOX */
}

/* GENERAL */

.spinner {
    padding: 5px;
    position: absolute;
    right: 0;
}
grails-app/views/layouts/main.gsp

<link rel="stylesheet"
 href="${resource(dir:'css',file:'main.css')}" />
Internationalisation
grails-app/i18l/
messages_fr.properties             messages.properties

default.home.label=Accueuil        default.home.label=Home
default.list.label=Liste de {0}s   default.list.label={0} List
default.add.label=Ajouter {0}      default.add.label=Add {0}
default.new.label=Nouveau {0}      default.new.label=New {0}
default.create.label=Créer {0}     default.create.label=Create {0}
default.show.label=Voir {0}        default.show.label=Show {0}
default.edit.label=Modifier {0}    default.edit.label=Edit {0}
lang=fr
lang=en
Internationalisation
                     Langue de départ
grails-app/conf/spring/resources.groovy
// Place your Spring DSL code here
import org.springframework.web.servlet.i18n.SessionLocaleResolver

beans = {
   localeResolver(SessionLocaleResolver) {
      defaultLocale= Locale.FRENCH
      Locale.setDefault (Locale.FRENCH)
   }
}


                       Charset/encoding
grails-app/conf/config.groovy
grails.views.gsp.encoding = "UTF-8"
grails.converters.encoding = "UTF-8"

                                             waq-o-theque v0.02
ORM: de l’oject à la BD
                    Object Relation Mapping
•Pour les objets du domaine
•Le schéma est créé par l’application ou en utilisant une BD
existante
•Le datatype est deviné à partir de la définition des classes
•monObject.save()
•MaClasse.get(id)
•MaClasse.findAll()
•Livre.findAllByAuthor(“Sartre”)
•Relations entre object
  - 1..1
  - 1..[0,1]
  - 1..[0,n]
  - n..1
  - n..n (avec “join table” ou “join object”)
$ grails create-domain-class 
> com.nancydeschenes.waqotheque.Personne

package com.nancydeschenes.waqotheque

class Personne {

    static constraints = {
      prenom(blank:false, nullable: false)
      nom(blank:true, nullable:true)
      fiabilite(blank:true, nullable:true, min:0, max:5)
    }
	
    static hasMany = [prets: Pret]
	
    String prenom;
    String nom;
    Integer fiabilite // 0: pas fiable; 5: je lui fais confiance	
}
$ grails generate-all 
> com.nancydeschenes.waqotheque.Personne


                Contrôleur CRUD:
                •create
                •read
                •update
                •delete
grails-app/views/personne/create.gsp
<g:textField name="fiabilite"
   value="${fieldValue(
            bean: personneInstance,
            field: 'fiabilite')}"
   size="3" />
ORM: Contraintes
                         Contraintes
Personne.groovy
static constraints = {
  prenom(blank:false, nullable: false)
  nom(blank:true, nullable:true)
  fiabilite(blank:true, nullable:true, min:0, max:5)
}

messages_fr.properties
default.invalid.max.size.message=La propriété
    [{0}] de la classe [{1}] avec la valeur [{2}]
    est supérieure à la valeur maximum [{3}]
default.invalid.min.size.message=La propriété
    [{0}] de la classe [{1}] avec la valeur [{2}]
    est inférieure à la valeur minimum [{3}]
Bien.groovy
    static constraints = {
	 	 nom(blank: false, nullable:false, unique:true)
	 	 description(nullable:true)
    }


messages_fr.properties
default.not.unique.message=La propriété [{0}]
    de la classe [{1}] avec la valeur [{2}]
    doit être unique
DVD.nom.unique=Vous avez déjà enregistré un
    bien appelé {2}.
waq-o-theque V0.03
ORM: Relations entre objets



Personne                    Bien

      1                     1
                     0..n


              Prêt
Bien.groovy                         Personne.groovy


   static hasMany = [prets: Pret]
                                       static hasMany = [prets: Pret]




       Pret.groovy



        static belongsTo = [emprunteur: Personne, bienPrete: Bien]
Contrôleurs
  Controllers
Personne.groovy
static niveauxDeFiabilite = ['pas fiable',
    'très peu fiable', 'peu fiable', 'moyennement fiable',
    'plutôt fiable', 'très fiable']
	

String toString() {
	 	 "${prenom} ${nom} (${niveauxDeFiabilite[fiabilite]})"
	 }



Bien.groovy
	 String toString() {
	 	 nom
	 }
Modifier les .gsps
$ grails generate-views 
> com.nancydeschenes.waqotheque.Pret

.../views/pret/create.gsp
<g:datePicker name="dateRetour" precision="day"
value="${pretInstance?.dateEmprunt}" />




<g:datePicker name="dateRetour" precision="day"
value="${pretInstance?.dateRetour?: 'none'}"
noSelection="['':'À  venir']"/>
waq-o-theque v0.04
Taglib

$ grails create-taglib 
> com.nancydeschenes.waqotheque.Personne
pret/list.gsp
avant: <td>
          ${fieldValue(bean: pretInstance,
            field: "emprunteur")}
        </td>

après: <td>
          <g:personne
            value="${pretInstance?.emprunteur}"/>
        </td>
grails-app/taglibs/PersonneTagLib.groovy
package com.nancydeschenes.waqotheque

class PersonneTagLib {
	 def personne = { attr, body ->
	 	 Personne personneInstance = attr['value']
	 	 if (!personneInstance) {
	 	 	 throwTagError("Tag [personne] is missing required
attribute [value]")
	 	 }
	 	 def linkColor = "black";
	 	 switch (personneInstance.fiabilite) {
	 	 	 case 5: linkColor = "green"; break
	 	 	 case 4..3 : linkColor = "yellow"; break
	 	 	 case 2..1 : linkColor = "orange"; break
	 	 	 case 0: linkColor = "red"; break
	 	 }
out << """<a href="${g:createLink(
                         controller: 'personne',
                         action:'show',
                         id:personneInstance.id)}”
            style="color: ${linkColor}">
	 	 	 	 	 ${personneInstance.prenom} ${personneInstance.nom}
	 	 </a>"""
	 }
}
Taglib: namespace
views/pret/list.gsp
<waquotheque:personne
    value="${pretInstance?.emprunteur}"/>

grails-app/taglibs/com/.../PersonneTagLib.groovy
	 static namespace = 'waqotheque'




                                           waq-o-theque v0.05
Services
$ grails create-service 
> com.nancydeschenes.waqotheque.ConseilsPrets

grails-app/services/com/.../ConseilsPretsServices

package com.nancydeschenes.waqotheque

class ConseilPretsService {
  static transactional = false
  def beneficeDuDoute = 40;
	
  def devraitPreter(Personne aQui, Bien quoi) {
    // Formule super compliquée... ou pas!
    // 0..5 -> 0..100, puis on applique le bénéfice du doute
    return aQui.fiabilite * 20 > beneficeDuDoute
  }
}
grails-app/controllers/com/.../ConseilsPretsServices

ConseilPretsService conseilPretsService;

ou
def conseilPretsService;
grails-app/controllers/com/.../ConseilsPretsServices

def save = {
  def pretInstance = new Pret(params)
  if (!conseilPretsService
       .devraitPreter(pretInstance.emprunteur,
                    pretInstance.bienPrete)) {
    flash.message = "Ce pr&ecirc;t n'est pas une " +
                    "bonne id&eacute;e"
    render(view: "create",
           model:[pretInstance: pretInstance])
    return;
  }
grails-app/controllers/com/.../ConseilsPretsServices (suite)
  if (pretInstance.save(flush: true)) {
    flash.message =
      """${message(code: 'default.created.message',
        args: [message(code: 'pret.label',
                       default: 'Pret'),
               pretInstance.id])}"
      redirect(action: "show", id: pretInstance.id)
    } else {
      render(view: "create",
             model: [pretInstance: pretInstance])
	 	 }
	 }
waq-o-theque V0.06
AJAX
PretController.groovy

def ajaxEstCeUneBonneIdee = {
  Personne emprunteur =
    Personne.get(params['emprunteur.id']);
  if (conseilPretsService
      .devraitPreter(emprunteur)) {
    render "Bon choix"
  } else {
    render "Ah &ccedil;a, &ccedil;'est " +
           "pas une bonne id&eacute;e"
	 	 }
	 }
ConseilPretsService.groovy

package com.nancydeschenes.waqotheque

class ConseilPretsService {
  static transactional = false
  def beneficeDuDoute = 40;

    def devraitPreter(Personne aQui, Bien quoi) {
      // A formule pourait utiliser le bien, mais
      // pour l'instant, allons-y avec l'emprunteur seulement
      devraitPreter(aQui)
    }
	
    def devraitPreter(Personne aQui) {
      // Formule super compliquée... ou pas!
      // 0..5 -> 0..100, puis on applique le bénéfice du doute	 	
      return aQui.fiabilite * 20 > beneficeDuDoute
    }
}
views/pret/create.gsp ou views/layout/main.gsp
<g:javascript library="prototype"/>




views/pret/create.gsp
<g:select name="emprunteur.id"
   from="${com.nancydeschenes.waqotheque.Personne.list()}"
   optionKey="id" value="${pretInstance?.emprunteur?.id}"
   onchange="miseAJourStatutEmprunteur()" />
 <span id="bonneIdee">?</span>
views/pret/create.gsp (suite)

<g:javascript>
  function miseAJourStatutEmprunteur() {
    var select =
      document.getElementById('emprunteur.id');
      ${g.remoteFunction(controller:'pret',
          action: 'ajaxEstCeUneBonneIdee',
          params:''emprunteur.id=' + select.value',
          update: "bonneIdee")}
  }
  window.onload = miseAJourStatutEmprunteur
</g:javascript>
waq-o-theque v0.01
Plugins

•Security   •Quartz       •JodaTime
•Email      •Recherche    •Twitter
•JQuery     (solr, etc)   •Facebook
•Mobile     •GWT          •OpenSocial
•NoSQL      •FCKEditor    •CMS
IDE

•Eclipse: SpringSource Tool Suite
•IDEA: IntelliJ
•Netbeans
•vi/emacs
Cloud

                  JVM?
               Ça marche!

     Même sur Google AppEngine?
Oui! mais il faut faire attention aux APIs,
       et ça prends “always on”
Ressources
                   www.grails.org



              user@grails.codehaus.org
       (http://grails.1312388.n4.nabble.com/)



http://forum.springsource.org/forumdisplay.php?f=76
https://github.com/nancyd/waq-o-theque/
Nancy Deschênes

 deschenes.nancy@gmail.com

        @ndeschenes

  https://github.com/nancyd/

http://blog.nancydeschenes.com

Contenu connexe

Tendances

DrupalCamp Lyon 2012 - Optimiser les performances Drupal depuis les tranchées
DrupalCamp Lyon 2012 -  Optimiser les performances Drupal depuis les tranchéesDrupalCamp Lyon 2012 -  Optimiser les performances Drupal depuis les tranchées
DrupalCamp Lyon 2012 - Optimiser les performances Drupal depuis les tranchées
Aurelien Navarre
 
Meetup Drupal Lyon - Sécuriser un site drupal
Meetup Drupal Lyon - Sécuriser un site drupalMeetup Drupal Lyon - Sécuriser un site drupal
Meetup Drupal Lyon - Sécuriser un site drupal
Aurelien Navarre
 

Tendances (14)

Meet-Up SQLI Lyon 09-2015 - Varnish
Meet-Up SQLI Lyon 09-2015 - VarnishMeet-Up SQLI Lyon 09-2015 - Varnish
Meet-Up SQLI Lyon 09-2015 - Varnish
 
Guss webcasts Query Memory Grants - june 2013
Guss webcasts   Query Memory Grants - june 2013Guss webcasts   Query Memory Grants - june 2013
Guss webcasts Query Memory Grants - june 2013
 
JSS2014 – Haute disponibilité dans Azure
JSS2014 – Haute disponibilité dans AzureJSS2014 – Haute disponibilité dans Azure
JSS2014 – Haute disponibilité dans Azure
 
Bases de données Spatiales - POSTGIS
Bases de données Spatiales - POSTGISBases de données Spatiales - POSTGIS
Bases de données Spatiales - POSTGIS
 
Tout ce que le getting started mongo db ne vous dira pas
Tout ce que le getting started mongo db ne vous dira pasTout ce que le getting started mongo db ne vous dira pas
Tout ce que le getting started mongo db ne vous dira pas
 
PostgreSQL sous linux
PostgreSQL sous linuxPostgreSQL sous linux
PostgreSQL sous linux
 
DrupalCamp Lyon 2012 - Optimiser les performances Drupal depuis les tranchées
DrupalCamp Lyon 2012 -  Optimiser les performances Drupal depuis les tranchéesDrupalCamp Lyon 2012 -  Optimiser les performances Drupal depuis les tranchées
DrupalCamp Lyon 2012 - Optimiser les performances Drupal depuis les tranchées
 
Javascript : fondamentaux et OOP
Javascript : fondamentaux et OOPJavascript : fondamentaux et OOP
Javascript : fondamentaux et OOP
 
Mariadb pour les developpeurs - OSDC
Mariadb pour les developpeurs - OSDCMariadb pour les developpeurs - OSDC
Mariadb pour les developpeurs - OSDC
 
Procédures CLR pour SQL Server : avantages et inconvénients
Procédures CLR pour SQL Server : avantages et inconvénientsProcédures CLR pour SQL Server : avantages et inconvénients
Procédures CLR pour SQL Server : avantages et inconvénients
 
jQuery
jQueryjQuery
jQuery
 
R Devtools
R DevtoolsR Devtools
R Devtools
 
installation et configuration du DNS sous Windows serveur 2003
installation et configuration du DNS sous Windows serveur 2003installation et configuration du DNS sous Windows serveur 2003
installation et configuration du DNS sous Windows serveur 2003
 
Meetup Drupal Lyon - Sécuriser un site drupal
Meetup Drupal Lyon - Sécuriser un site drupalMeetup Drupal Lyon - Sécuriser un site drupal
Meetup Drupal Lyon - Sécuriser un site drupal
 

En vedette (6)

Migrating from Grails 2 to Grails 3
Migrating from Grails 2 to Grails 3Migrating from Grails 2 to Grails 3
Migrating from Grails 2 to Grails 3
 
Data vault
Data vaultData vault
Data vault
 
High Availability Hadoop
High Availability HadoopHigh Availability Hadoop
High Availability Hadoop
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
Metaprogramming Techniques In Groovy And Grails
Metaprogramming Techniques In Groovy And GrailsMetaprogramming Techniques In Groovy And Grails
Metaprogramming Techniques In Groovy And Grails
 
Grails Layouts & Sitemesh
Grails Layouts & SitemeshGrails Layouts & Sitemesh
Grails Layouts & Sitemesh
 

Similaire à 1 2-3-grails

Android rendu et performance - 17 avril 2012
Android rendu et performance - 17 avril 2012Android rendu et performance - 17 avril 2012
Android rendu et performance - 17 avril 2012
Paris Android User Group
 
Active Directory Sur Windows 2008 R2
Active  Directory Sur  Windows 2008  R2Active  Directory Sur  Windows 2008  R2
Active Directory Sur Windows 2008 R2
SIMOES AUGUSTO
 
Grails from scratch to prod - MixIT 2011
Grails from scratch to prod - MixIT 2011Grails from scratch to prod - MixIT 2011
Grails from scratch to prod - MixIT 2011
Aurélien Maury
 

Similaire à 1 2-3-grails (20)

Android rendu et performance - 17 avril 2012
Android rendu et performance - 17 avril 2012Android rendu et performance - 17 avril 2012
Android rendu et performance - 17 avril 2012
 
Active Directory Sur Windows 2008 R2
Active  Directory Sur  Windows 2008  R2Active  Directory Sur  Windows 2008  R2
Active Directory Sur Windows 2008 R2
 
Les fonctions MariaDB - LeMug.fr
Les fonctions MariaDB - LeMug.frLes fonctions MariaDB - LeMug.fr
Les fonctions MariaDB - LeMug.fr
 
Demonstration injection de code
Demonstration injection de codeDemonstration injection de code
Demonstration injection de code
 
Sécurité MySQL
Sécurité MySQLSécurité MySQL
Sécurité MySQL
 
Orchestration
OrchestrationOrchestration
Orchestration
 
Orchestration
OrchestrationOrchestration
Orchestration
 
Performance et optimisation de PrestaShop
Performance et optimisation de PrestaShopPerformance et optimisation de PrestaShop
Performance et optimisation de PrestaShop
 
Gestion des index SQL : Soyez AWARE !
Gestion des index SQL : Soyez  AWARE !Gestion des index SQL : Soyez  AWARE !
Gestion des index SQL : Soyez AWARE !
 
Grails from scratch to prod - MixIT 2011
Grails from scratch to prod - MixIT 2011Grails from scratch to prod - MixIT 2011
Grails from scratch to prod - MixIT 2011
 
Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)Présentation Javascript à l'ESI (Alger)
Présentation Javascript à l'ESI (Alger)
 
22410 b 04
22410 b 0422410 b 04
22410 b 04
 
Administration oracle7
Administration oracle7Administration oracle7
Administration oracle7
 
Hibernate.pdf
Hibernate.pdfHibernate.pdf
Hibernate.pdf
 
22410B_04.pptx bdsbsdhbsbdhjbhjdsbhbhbdsh
22410B_04.pptx bdsbsdhbsbdhjbhjdsbhbhbdsh22410B_04.pptx bdsbsdhbsbdhjbhjdsbhbhbdsh
22410B_04.pptx bdsbsdhbsbdhjbhjdsbhbhbdsh
 
Absorber les web pour constituer un corpus internet : My Web intelligence.
Absorber les web pour constituer un corpus internet : My Web intelligence.Absorber les web pour constituer un corpus internet : My Web intelligence.
Absorber les web pour constituer un corpus internet : My Web intelligence.
 
Azure Camp 9 Décembre - slides session développeurs webmedia
Azure Camp 9 Décembre - slides session développeurs webmediaAzure Camp 9 Décembre - slides session développeurs webmedia
Azure Camp 9 Décembre - slides session développeurs webmedia
 
Codedarmor 2012 - 06/03 - HTML5, CSS3 et Javascript
Codedarmor 2012 - 06/03 - HTML5, CSS3 et JavascriptCodedarmor 2012 - 06/03 - HTML5, CSS3 et Javascript
Codedarmor 2012 - 06/03 - HTML5, CSS3 et Javascript
 
Docker, ça mange quoi au printemps
Docker, ça mange quoi au printempsDocker, ça mange quoi au printemps
Docker, ça mange quoi au printemps
 
Julien Maitrehenry - Docker, ça mange quoi au printemps
Julien Maitrehenry - Docker, ça mange quoi au printempsJulien Maitrehenry - Docker, ça mange quoi au printemps
Julien Maitrehenry - Docker, ça mange quoi au printemps
 

1 2-3-grails

  • 1. À VOS MARQUES... 1, 2, 3 GRAILS! Des applications pour le web vite, facile, et “fun”
  • 2. Grails Un “framework” pour developer des applications pour le web Déploiment vers JVM AJAX/xml/JSON ORM i18n: internationalisation Groovy Une fois, c’est assez TDD/Mocks/Stubs DRY: Don’t repeat yourself Sitemesh Contrôleurs Inversion du contrôle/injection de dépendances (IoC/DI: Inversion of control/dependency injection) Plugins Conventions/configuration GSP Taglibs
  • 3. Groovy •Compilé •Dynamique •Exécuté sur JVM •Lambdas et closures •Strings dynamiques - GStrings •Syntaxe aisée: - ; pas nécessaires ne sont pas requis - manipulations de listes - manipulations de “beans”/propriétés - méthodes sur les listes: ex: l.each {}
  • 4. $ grails create-project > waq-o-theque $ cd waq-o-theque $ ls application.properties grails-app lib scripts src test web-app
  • 5. $ ls grails-app conf controllers domain i18n services taglib utils $ grails create-domain-class > com.nancydeschenes.waqotheque.Bien
  • 6. package com.nancydeschenes.waqotheque class Bien {   static constraints = {   } }
  • 7. package com.nancydeschenes.waqotheque class Bien {   static constraints = { nom(blank: false, nullable:false, unique:true) description(nullable:true)   } String nom String description }
  • 8. package com.nancydeschenes.waqotheque class Livre extends Bien {   static constraints = {   } String auteur }
  • 9. package com.nancydeschenes.waqotheque class CD extends Bien { static constraints = { interprete(nullable:true)   } String interprete }
  • 10. package com.nancydeschenes.waqotheque class DVD extends Bien {   static constraints = { genre(nullable:true)   } String genre }
  • 11. $ grails create-controller > com.nancydeschenes.waqotheque.Livre package com.nancydeschenes.waqotheque class LivreController { def scaffold = true; }
  • 12. $ grails run-app Welcome to Grails 1.3.6 - http:// grails.org/ Licensed under Apache Standard License 2.0 Grails home is set to: /Developer/ STS-2.5.1/grails-1.3.6 [...] Running Grails application.. Server running. Browse to http:// localhost:8080/waq-o-theque
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18. dataSource { pooled = true driverClassName = "org.hsqldb.jdbcDriver" username = "sa" password = "" } hibernate { cache.use_second_level_cache = true cache.use_query_cache = true cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider' }
  • 19. // environment specific settings environments { development { dataSource { dbCreate = "create-drop" // one of 'create', 'create- drop','update' url = "jdbc:hsqldb:mem:devDB" } } test { dataSource { dbCreate = "update" url = "jdbc:hsqldb:mem:testDb" } } production { dataSource { dbCreate = "update" url = "jdbc:hsqldb:file:prodDb;shutdown=true" } } }
  • 20. dataSource { pooled = true driverClassName = "com.mysql.jdbc.Driver" username = "waq" password = "waq" } hibernate { cache.use_second_level_cache = true cache.use_query_cache = true cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider' }
  • 21. // environment specific settings environments { development { dataSource { dbCreate = "update" // one of 'create', 'create- drop','update' url = "jdbc:mysql://localhost:3306/waqotheque_dev” show_sql = true } } test { dataSource { dbCreate = "update" url = "jdbc:mysql://localhost:3306/waqotheque_test" } } production { dataSource { dbCreate = "update" url = "jdbc:mysql://localhost:3306/waqotheque" } } }
  • 22. sql> show tables; -------------------------+ Tables_in_waqotheque_dev | -------------------------+ bien | -------------------------+ row in set (0.00 sec) sql> desc bien; ------------+--------------+------+-----+---------+----------------+ Field | Type | Null | Key | Default | Extra | ------------+--------------+------+-----+---------+----------------+ id | bigint(20) | NO | PRI | NULL | auto_increment | version | bigint(20) | NO | | NULL | | description | varchar(255) | YES | | NULL | | nom | varchar(255) | NO | UNI | NULL | | class | varchar(255) | NO | | NULL | | genre | varchar(255) | YES | | NULL | | interprete | varchar(255) | YES | | NULL | | auteur | varchar(255) | YES | | NULL | | ------------+--------------+------+-----+---------+----------------+ rows in set (0.00 sec)
  • 23.
  • 24. mysql> select id, version, description from bien; +----+---------+--------------------------------------------------+ | id | version | description | +----+---------+--------------------------------------------------+ | 1 | 0 | Un bon vieux livre... | | 2 | 0 | Un roman... | | 3 | 0 | L'influence de la musique sur notre comportement | | 4 | 0 | NULL | +----+---------+--------------------------------------------------+ 4 rows in set (0.00 sec) mysql> select id, nom, class from bien; +----+-----------------------+-------------------------------------+ | id | nom | class | +----+-----------------------+-------------------------------------+ | 1 | Martine ? l'?cole | com.nancydeschenes.waqotheque.Livre | | 2 | Au Bonheur des Ogres | com.nancydeschenes.waqotheque.Livre | | 3 | De la note au cerveau | com.nancydeschenes.waqotheque.Livre | | 4 | Les portes de Qu?bec | com.nancydeschenes.waqotheque.Livre | +----+-----------------------+-------------------------------------+ 4 rows in set (0.01 sec)
  • 25. mysql> select id, interprete, genre, auteur from bien; +----+-------------+---------+----------------------------------+ | id | interprete | genre | auteur | +----+-------------+---------+----------------------------------+ | 1 | NULL | NULL | Gilbert Delahaye, Marcel Marlier | | 2 | NULL | NULL | Daniel Pennac | | 3 | NULL | NULL | Daniel J. Levitin | | 4 | NULL | NULL | Jean-Pierre Charland | +----+-------------+---------+----------------------------------+ 6 rows in set (0.00 sec)
  • 26. SELECT * from bien;
  • 27. package com.nancydeschenes.waqotheque class CDController {     def scaffold = true; } package com.nancydeschenes.waqotheque class DVDController { def scaffold = true; }
  • 28.
  • 29. mysql> select id, version, description from bien; +----+---------+--------------------------------------------------+ | id | version | description | +----+---------+--------------------------------------------------+ | 1 | 0 | Un bon vieux livre... | | 2 | 0 | Un roman... | | 3 | 0 | L'influence de la musique sur notre comportement | | 4 | 0 | NULL | | 5 | 0 | L'histore d'un ogre | | 6 | 0 | Album of the year | +----+---------+--------------------------------------------------+ 6 rows in set (0.00 sec) mysql> select id, nom, class from bien; +----+-----------------------+-------------------------------------+ | id | nom | class | +----+-----------------------+-------------------------------------+ | 1 | Martine ? l'?cole | com.nancydeschenes.waqotheque.Livre | | 2 | Au Bonheur des Ogres | com.nancydeschenes.waqotheque.Livre | | 3 | De la note au cerveau | com.nancydeschenes.waqotheque.Livre | | 4 | Les portes de Qu?bec | com.nancydeschenes.waqotheque.Livre | | 5 | Shrek | com.nancydeschenes.waqotheque.DVD | | 6 | The Suburbs | com.nancydeschenes.waqotheque.CD | +----+-----------------------+-------------------------------------+ 6 rows in set (0.01 sec)
  • 30. mysql> select id, interprete, genre, auteur from bien; +----+-------------+---------+----------------------------------+ | id | interprete | genre | auteur | +----+-------------+---------+----------------------------------+ | 1 | NULL | NULL | Gilbert Delahaye, Marcel Marlier | | 2 | NULL | NULL | Daniel Pennac | | 3 | NULL | NULL | Daniel J. Levitin | | 4 | NULL | NULL | Jean-Pierre Charland | | 5 | NULL | Enfants | NULL | | 6 | Arcade Fire | NULL | NULL | +----+-------------+---------+----------------------------------+ 6 rows in set (0.00 sec)
  • 31. SELECT * from bien;
  • 32. Structure de l’application Grails grails-app/ conf/ controllers/ domain/ i18l/ services/ taglib/ utils views src/ java/ groovy/ waq-o-theque v0.01
  • 33. Sitemesh grails-app/views/layouts/main.gsp <!DOCTYPE html> <html> <head> <title><g:layoutTitle default="Grails" /></title> <link rel="stylesheet" href="$ {resource(dir:'css',file:'main.css')}" /> <link rel="shortcut icon" href="$ {resource(dir:'images',file:'favicon.ico')}" type="image/x-icon" / > <g:layoutHead /> <g:javascript library="application" /> </head> grails-app/views/index.gsp <head> <title>La WAQ-o-theque</title> <meta name="layout" content="main" />
  • 34. <body> <div id="spinner" class="spinner" style="display:none;"> <img src="${resource(dir:'images',file:'spinner.gif')}" alt="${message(code:'spinner.alt',default:'Loading...')}" /> </div> <div id="grailsLogo"><a href="http://grails.org"><img src="${resource(dir:'images',file:'grails_logo.png')}" alt="Grails" border="0" /></a></div> <g:layoutBody /> </body> </html> <body> <div id="body" style="font-size: 30px;font-weight:bold"> Bienvenue au site de la WAQ-o-theque! </div> </body>
  • 35. Style CSS web-app/css/main.css html * { margin: 0; /*padding: 0; SELECT NOT DISPLAYED CORRECTLY IN FIREFOX */ } /* GENERAL */ .spinner { padding: 5px; position: absolute; right: 0; }
  • 37. Internationalisation grails-app/i18l/ messages_fr.properties messages.properties default.home.label=Accueuil default.home.label=Home default.list.label=Liste de {0}s default.list.label={0} List default.add.label=Ajouter {0} default.add.label=Add {0} default.new.label=Nouveau {0} default.new.label=New {0} default.create.label=Créer {0} default.create.label=Create {0} default.show.label=Voir {0} default.show.label=Show {0} default.edit.label=Modifier {0} default.edit.label=Edit {0}
  • 39. Internationalisation Langue de départ grails-app/conf/spring/resources.groovy // Place your Spring DSL code here import org.springframework.web.servlet.i18n.SessionLocaleResolver beans = {    localeResolver(SessionLocaleResolver) {       defaultLocale= Locale.FRENCH       Locale.setDefault (Locale.FRENCH)    } } Charset/encoding grails-app/conf/config.groovy grails.views.gsp.encoding = "UTF-8" grails.converters.encoding = "UTF-8" waq-o-theque v0.02
  • 40. ORM: de l’oject à la BD Object Relation Mapping •Pour les objets du domaine •Le schéma est créé par l’application ou en utilisant une BD existante •Le datatype est deviné à partir de la définition des classes •monObject.save() •MaClasse.get(id) •MaClasse.findAll() •Livre.findAllByAuthor(“Sartre”) •Relations entre object - 1..1 - 1..[0,1] - 1..[0,n] - n..1 - n..n (avec “join table” ou “join object”)
  • 41. $ grails create-domain-class > com.nancydeschenes.waqotheque.Personne package com.nancydeschenes.waqotheque class Personne { static constraints = { prenom(blank:false, nullable: false) nom(blank:true, nullable:true) fiabilite(blank:true, nullable:true, min:0, max:5) } static hasMany = [prets: Pret] String prenom; String nom; Integer fiabilite // 0: pas fiable; 5: je lui fais confiance }
  • 42. $ grails generate-all > com.nancydeschenes.waqotheque.Personne Contrôleur CRUD: •create •read •update •delete
  • 43. grails-app/views/personne/create.gsp <g:textField name="fiabilite" value="${fieldValue( bean: personneInstance, field: 'fiabilite')}" size="3" />
  • 44. ORM: Contraintes Contraintes Personne.groovy static constraints = { prenom(blank:false, nullable: false) nom(blank:true, nullable:true) fiabilite(blank:true, nullable:true, min:0, max:5) } messages_fr.properties default.invalid.max.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}] default.invalid.min.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
  • 45.
  • 46. Bien.groovy static constraints = { nom(blank: false, nullable:false, unique:true) description(nullable:true) } messages_fr.properties default.not.unique.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] doit être unique DVD.nom.unique=Vous avez déjà enregistré un bien appelé {2}.
  • 48. ORM: Relations entre objets Personne Bien 1 1 0..n Prêt
  • 49. Bien.groovy Personne.groovy static hasMany = [prets: Pret] static hasMany = [prets: Pret] Pret.groovy static belongsTo = [emprunteur: Personne, bienPrete: Bien]
  • 51. Personne.groovy static niveauxDeFiabilite = ['pas fiable', 'très peu fiable', 'peu fiable', 'moyennement fiable', 'plutôt fiable', 'très fiable'] String toString() { "${prenom} ${nom} (${niveauxDeFiabilite[fiabilite]})" } Bien.groovy String toString() { nom }
  • 52.
  • 53. Modifier les .gsps $ grails generate-views > com.nancydeschenes.waqotheque.Pret .../views/pret/create.gsp <g:datePicker name="dateRetour" precision="day" value="${pretInstance?.dateEmprunt}" /> <g:datePicker name="dateRetour" precision="day" value="${pretInstance?.dateRetour?: 'none'}" noSelection="['':'À  venir']"/>
  • 54.
  • 56. Taglib $ grails create-taglib > com.nancydeschenes.waqotheque.Personne pret/list.gsp avant: <td> ${fieldValue(bean: pretInstance, field: "emprunteur")} </td> après: <td> <g:personne value="${pretInstance?.emprunteur}"/> </td>
  • 57. grails-app/taglibs/PersonneTagLib.groovy package com.nancydeschenes.waqotheque class PersonneTagLib { def personne = { attr, body -> Personne personneInstance = attr['value'] if (!personneInstance) { throwTagError("Tag [personne] is missing required attribute [value]") } def linkColor = "black"; switch (personneInstance.fiabilite) { case 5: linkColor = "green"; break case 4..3 : linkColor = "yellow"; break case 2..1 : linkColor = "orange"; break case 0: linkColor = "red"; break }
  • 58. out << """<a href="${g:createLink( controller: 'personne', action:'show', id:personneInstance.id)}” style="color: ${linkColor}"> ${personneInstance.prenom} ${personneInstance.nom} </a>""" } }
  • 59.
  • 60. Taglib: namespace views/pret/list.gsp <waquotheque:personne value="${pretInstance?.emprunteur}"/> grails-app/taglibs/com/.../PersonneTagLib.groovy static namespace = 'waqotheque' waq-o-theque v0.05
  • 61. Services $ grails create-service > com.nancydeschenes.waqotheque.ConseilsPrets grails-app/services/com/.../ConseilsPretsServices package com.nancydeschenes.waqotheque class ConseilPretsService { static transactional = false def beneficeDuDoute = 40; def devraitPreter(Personne aQui, Bien quoi) { // Formule super compliquée... ou pas! // 0..5 -> 0..100, puis on applique le bénéfice du doute return aQui.fiabilite * 20 > beneficeDuDoute } }
  • 63. grails-app/controllers/com/.../ConseilsPretsServices def save = { def pretInstance = new Pret(params) if (!conseilPretsService .devraitPreter(pretInstance.emprunteur, pretInstance.bienPrete)) { flash.message = "Ce pr&ecirc;t n'est pas une " + "bonne id&eacute;e" render(view: "create", model:[pretInstance: pretInstance]) return; }
  • 64. grails-app/controllers/com/.../ConseilsPretsServices (suite) if (pretInstance.save(flush: true)) { flash.message = """${message(code: 'default.created.message', args: [message(code: 'pret.label', default: 'Pret'), pretInstance.id])}" redirect(action: "show", id: pretInstance.id) } else { render(view: "create", model: [pretInstance: pretInstance]) } }
  • 66. AJAX PretController.groovy def ajaxEstCeUneBonneIdee = { Personne emprunteur = Personne.get(params['emprunteur.id']); if (conseilPretsService .devraitPreter(emprunteur)) { render "Bon choix" } else { render "Ah &ccedil;a, &ccedil;'est " + "pas une bonne id&eacute;e" } }
  • 67. ConseilPretsService.groovy package com.nancydeschenes.waqotheque class ConseilPretsService { static transactional = false def beneficeDuDoute = 40; def devraitPreter(Personne aQui, Bien quoi) { // A formule pourait utiliser le bien, mais // pour l'instant, allons-y avec l'emprunteur seulement devraitPreter(aQui) } def devraitPreter(Personne aQui) { // Formule super compliquée... ou pas! // 0..5 -> 0..100, puis on applique le bénéfice du doute return aQui.fiabilite * 20 > beneficeDuDoute } }
  • 68. views/pret/create.gsp ou views/layout/main.gsp <g:javascript library="prototype"/> views/pret/create.gsp <g:select name="emprunteur.id" from="${com.nancydeschenes.waqotheque.Personne.list()}" optionKey="id" value="${pretInstance?.emprunteur?.id}" onchange="miseAJourStatutEmprunteur()" /> <span id="bonneIdee">?</span>
  • 69. views/pret/create.gsp (suite) <g:javascript> function miseAJourStatutEmprunteur() { var select = document.getElementById('emprunteur.id'); ${g.remoteFunction(controller:'pret', action: 'ajaxEstCeUneBonneIdee', params:''emprunteur.id=' + select.value', update: "bonneIdee")} } window.onload = miseAJourStatutEmprunteur </g:javascript>
  • 70.
  • 72. Plugins •Security •Quartz •JodaTime •Email •Recherche •Twitter •JQuery (solr, etc) •Facebook •Mobile •GWT •OpenSocial •NoSQL •FCKEditor •CMS
  • 73. IDE •Eclipse: SpringSource Tool Suite •IDEA: IntelliJ •Netbeans •vi/emacs
  • 74. Cloud JVM? Ça marche! Même sur Google AppEngine? Oui! mais il faut faire attention aux APIs, et ça prends “always on”
  • 75.
  • 76. Ressources www.grails.org user@grails.codehaus.org (http://grails.1312388.n4.nabble.com/) http://forum.springsource.org/forumdisplay.php?f=76
  • 78. Nancy Deschênes deschenes.nancy@gmail.com @ndeschenes https://github.com/nancyd/ http://blog.nancydeschenes.com

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \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. \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