1 2-3-grails

2,575 views

Published on

Présentation 1-2-3 Grails! tel que donné au Web à Quebec, le 24 février 2011.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,575
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
51
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • 1 2-3-grails

    1. 1. À VOS MARQUES... 1, 2, 3 GRAILS!Des applications pour le web vite, facile, et “fun”
    2. 2. GrailsUn “framework” pour developer des applications pour le web Déploiment vers JVM AJAX/xml/JSON ORM i18n: internationalisation Groovy Une fois, c’est assezTDD/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. 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. 4. $ grails create-project > waq-o-theque$ cd waq-o-theque$ ls application.properties grails-app lib scripts src test web-app
    5. 5. $ ls grails-app conf controllers domain i18n services taglib utils$ grails create-domain-class > com.nancydeschenes.waqotheque.Bien
    6. 6. package com.nancydeschenes.waqothequeclass Bien {  static constraints = {  }}
    7. 7. package com.nancydeschenes.waqothequeclass Bien {  static constraints = { nom(blank: false, nullable:false, unique:true) description(nullable:true)  } String nom String description}
    8. 8. package com.nancydeschenes.waqothequeclass Livre extends Bien {  static constraints = {  } String auteur}
    9. 9. package com.nancydeschenes.waqothequeclass CD extends Bien { static constraints = { interprete(nullable:true)  } String interprete}
    10. 10. package com.nancydeschenes.waqothequeclass DVD extends Bien {  static constraints = { genre(nullable:true)  } String genre}
    11. 11. $ grails create-controller > com.nancydeschenes.waqotheque.Livre package com.nancydeschenes.waqotheque class LivreController { def scaffold = true; }
    12. 12. $ grails run-appWelcome to Grails 1.3.6 - http://grails.org/Licensed under Apache StandardLicense 2.0Grails 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. 13. 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}
    14. 14. // environment specific settingsenvironments { 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" } }}
    15. 15. 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}
    16. 16. // environment specific settingsenvironments { 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" } }}
    17. 17. 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)
    18. 18. mysql> select id, version, description from bien;+----+---------+--------------------------------------------------+| id | version | description |+----+---------+--------------------------------------------------+| 1 | 0 | Un bon vieux livre... || 2 | 0 | Un roman... || 3 | 0 | Linfluence 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)
    19. 19. 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)
    20. 20. SELECT * from bien;
    21. 21. package com.nancydeschenes.waqothequeclass CDController {    def scaffold = true;}package com.nancydeschenes.waqothequeclass DVDController { def scaffold = true;}
    22. 22. mysql> select id, version, description from bien;+----+---------+--------------------------------------------------+| id | version | description |+----+---------+--------------------------------------------------+| 1 | 0 | Un bon vieux livre... || 2 | 0 | Un roman... || 3 | 0 | Linfluence de la musique sur notre comportement || 4 | 0 | NULL || 5 | 0 | Lhistore dun 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)
    23. 23. 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)
    24. 24. SELECT * from bien;
    25. 25. Structure de l’application Grailsgrails-app/ conf/ controllers/ domain/ i18l/ services/ taglib/ utils viewssrc/ java/ groovy/ waq-o-theque v0.01
    26. 26. Sitemeshgrails-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" />
    27. 27. <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"><imgsrc="${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>
    28. 28. Style CSSweb-app/css/main.csshtml * { margin: 0; /*padding: 0; SELECT NOT DISPLAYED CORRECTLY IN FIREFOX */}/* GENERAL */.spinner { padding: 5px; position: absolute; right: 0;}
    29. 29. grails-app/views/layouts/main.gsp<link rel="stylesheet" href="${resource(dir:css,file:main.css)}" />
    30. 30. Internationalisationgrails-app/i18l/messages_fr.properties messages.propertiesdefault.home.label=Accueuil default.home.label=Homedefault.list.label=Liste de {0}s default.list.label={0} Listdefault.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}
    31. 31. lang=frlang=en
    32. 32. Internationalisation Langue de départgrails-app/conf/spring/resources.groovy// Place your Spring DSL code hereimport org.springframework.web.servlet.i18n.SessionLocaleResolverbeans = {   localeResolver(SessionLocaleResolver) {      defaultLocale= Locale.FRENCH      Locale.setDefault (Locale.FRENCH)   }} Charset/encodinggrails-app/conf/config.groovygrails.views.gsp.encoding = "UTF-8"grails.converters.encoding = "UTF-8" waq-o-theque v0.02
    33. 33. 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 BDexistante•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”)
    34. 34. $ grails create-domain-class > com.nancydeschenes.waqotheque.Personnepackage com.nancydeschenes.waqothequeclass 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 }
    35. 35. $ grails generate-all > com.nancydeschenes.waqotheque.Personne Contrôleur CRUD: •create •read •update •delete
    36. 36. grails-app/views/personne/create.gsp<g:textField name="fiabilite" value="${fieldValue( bean: personneInstance, field: fiabilite)}" size="3" />
    37. 37. ORM: Contraintes ContraintesPersonne.groovystatic constraints = { prenom(blank:false, nullable: false) nom(blank:true, nullable:true) fiabilite(blank:true, nullable:true, min:0, max:5)}messages_fr.propertiesdefault.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}]
    38. 38. Bien.groovy static constraints = { nom(blank: false, nullable:false, unique:true) description(nullable:true) }messages_fr.propertiesdefault.not.unique.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] doit être uniqueDVD.nom.unique=Vous avez déjà enregistré un bien appelé {2}.
    39. 39. waq-o-theque V0.03
    40. 40. ORM: Relations entre objetsPersonne Bien 1 1 0..n Prêt
    41. 41. Bien.groovy Personne.groovy static hasMany = [prets: Pret] static hasMany = [prets: Pret] Pret.groovy static belongsTo = [emprunteur: Personne, bienPrete: Bien]
    42. 42. Contrôleurs Controllers
    43. 43. Personne.groovystatic 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 }
    44. 44. 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]"/>
    45. 45. waq-o-theque v0.04
    46. 46. Taglib$ grails create-taglib > com.nancydeschenes.waqotheque.Personnepret/list.gspavant: <td> ${fieldValue(bean: pretInstance, field: "emprunteur")} </td>après: <td> <g:personne value="${pretInstance?.emprunteur}"/> </td>
    47. 47. grails-app/taglibs/PersonneTagLib.groovypackage com.nancydeschenes.waqothequeclass PersonneTagLib { def personne = { attr, body -> Personne personneInstance = attr[value] if (!personneInstance) { throwTagError("Tag [personne] is missing requiredattribute [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 }
    48. 48. out << """<a href="${g:createLink( controller: personne, action:show, id:personneInstance.id)}” style="color: ${linkColor}"> ${personneInstance.prenom} ${personneInstance.nom} </a>""" }}
    49. 49. Taglib: namespaceviews/pret/list.gsp<waquotheque:personne value="${pretInstance?.emprunteur}"/>grails-app/taglibs/com/.../PersonneTagLib.groovy static namespace = waqotheque waq-o-theque v0.05
    50. 50. Services$ grails create-service > com.nancydeschenes.waqotheque.ConseilsPretsgrails-app/services/com/.../ConseilsPretsServicespackage com.nancydeschenes.waqothequeclass 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 }}
    51. 51. grails-app/controllers/com/.../ConseilsPretsServicesConseilPretsService conseilPretsService;oudef conseilPretsService;
    52. 52. grails-app/controllers/com/.../ConseilsPretsServicesdef save = { def pretInstance = new Pret(params) if (!conseilPretsService .devraitPreter(pretInstance.emprunteur, pretInstance.bienPrete)) { flash.message = "Ce pr&ecirc;t nest pas une " + "bonne id&eacute;e" render(view: "create", model:[pretInstance: pretInstance]) return; }
    53. 53. 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]) } }
    54. 54. waq-o-theque V0.06
    55. 55. AJAXPretController.groovydef 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" } }
    56. 56. ConseilPretsService.groovypackage com.nancydeschenes.waqothequeclass ConseilPretsService { static transactional = false def beneficeDuDoute = 40; def devraitPreter(Personne aQui, Bien quoi) { // A formule pourait utiliser le bien, mais // pour linstant, allons-y avec lemprunteur 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 }}
    57. 57. 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>
    58. 58. 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>
    59. 59. waq-o-theque v0.01
    60. 60. Plugins•Security •Quartz •JodaTime•Email •Recherche •Twitter•JQuery (solr, etc) •Facebook•Mobile •GWT •OpenSocial•NoSQL •FCKEditor •CMS
    61. 61. IDE•Eclipse: SpringSource Tool Suite•IDEA: IntelliJ•Netbeans•vi/emacs
    62. 62. Cloud JVM? Ça marche! Même sur Google AppEngine?Oui! mais il faut faire attention aux APIs, et ça prends “always on”
    63. 63. Ressources www.grails.org user@grails.codehaus.org (http://grails.1312388.n4.nabble.com/)http://forum.springsource.org/forumdisplay.php?f=76
    64. 64. https://github.com/nancyd/waq-o-theque/
    65. 65. Nancy Deschênes deschenes.nancy@gmail.com @ndeschenes https://github.com/nancyd/http://blog.nancydeschenes.com

    ×