<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>Choses à faire &#187; Développement</title> <atom:link href="http://www.chosesafaire.fr/tag/developpement/feed/" rel="self" type="application/rss+xml" /><link>http://www.chosesafaire.fr</link> <description>24h dans une journée, et tant de choses à faire !</description> <lastBuildDate>Tue, 02 Mar 2010 10:13:22 +0000</lastBuildDate> <generator>http://wordpress.org/?v=2.9.2</generator> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <item><title>Easyb, Behavior Driven Development</title><link>http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/</link> <comments>http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/#comments</comments> <pubDate>Tue, 02 Mar 2010 10:13:22 +0000</pubDate> <dc:creator>Killian Ebel</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[agile]]></category> <category><![CDATA[bdd]]></category> <category><![CDATA[groovy]]></category> <category><![CDATA[java]]></category> <category><![CDATA[tdd]]></category> <category><![CDATA[tests]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=310</guid> <description><![CDATA[Pour rappel, le concept de Behavior Driven Development propose de se focaliser sur le comportement attendu de l&#8217;application plutôt que sur les tests. Ces comportements peuvent être décrits soit sous la forme de spécifications, soit sous la forme d&#8217;histoires !Installation d&#8217;Easyb
Easyb est un outil permettant d&#8217;implémenter le BDD dans un projet Java, bien que le [...]Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2009/10/vraptor-le-framework-qui-a-les-crocs/' rel='bookmark' title='Permanent Link: VRaptor, le framework qui a les crocs'>VRaptor, le framework qui a les crocs</a></li><li><a href='http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/' rel='bookmark' title='Permanent Link: Mocks et Multithreading'>Mocks et Multithreading</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Pour rappel, le concept de <a href="http://fr.wikipedia.org/wiki/Behavior_Driven_Development">Behavior Driven Development</a> propose de se focaliser sur le comportement attendu de l&#8217;application plutôt que sur les tests. Ces comportements peuvent être décrits soit sous la forme de spécifications, soit sous la forme d&#8217;histoires !</p><p><span id="more-310"></span></p><h2>Installation d&#8217;Easyb</h2><p>Easyb est un outil permettant d&#8217;implémenter le BDD dans un projet Java, bien que le code à écrire pour rédiger vos spécifications sera du <a href="http://fr.wikipedia.org/wiki/Groovy_%28langage%29">Groovy</a>, dont la syntaxe est plus légère (plus agile) que Java. Il est comparable à l&#8217;outil <a href="http://code.google.com/p/specs/">specs</a> en <a href="http://fr.wikipedia.org/wiki/Scala_%28langage%29">Scala</a> ou encore <a href="http://rspec.info/">RSpec</a> en <a href="http://fr.wikipedia.org/wiki/Ruby">Ruby</a>.</p><p>Pour <a href="http://www.easyb.org/running.html">l&#8217;intégrer à votre projet</a>, vous disposez de multiples possibilités : ligne de commande, <a href="http://fr.wikipedia.org/wiki/Apache_Ant">Ant</a>, <a href="http://fr.wikipedia.org/wiki/Apache_Maven">Maven</a> ou encore l&#8217;intégration avec les <a href="http://fr.wikipedia.org/wiki/Environnement_de_d%C3%A9veloppement_int%C3%A9gr%C3%A9">IDE</a> <a href="http://www.jetbrains.com/idea/">IntelliJ</a> et <a href="http://www.eclipse.org/">Eclipse</a>.</p><p>Il m&#8217;a été impossible d&#8217;installer le plugin Eclipse, l&#8217;adresse du plugin donnée sur le site officiel n&#8217;ayant plus l&#8217;air d&#8217;exister ! Je me suis donc penché sur la solution Maven. Créons donc le projet :</p><pre class="brush: shell">
mvn archetype:create -DgroupId=fr.chosesafaire -DartifactId=easyb -Dpackagename=fr.chosesafaire
</pre><p>Editons le fichier de projet <strong>pom.xml</strong> pour y inclure Easyb, qui sera appelée lors de la phase <strong>test</strong> :</p><pre class="brush: xml">
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;

&lt;properties&gt;
    &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&lt;/properties&gt;

&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
&lt;groupId&gt;fr.chosesafaire&lt;/groupId&gt;
&lt;artifactId&gt;easyb&lt;/artifactId&gt;
&lt;packaging&gt;jar&lt;/packaging&gt;
&lt;name&gt;easyb&lt;/name&gt;
&lt;version&gt;0.0.1&lt;/version&gt;
&lt;description&gt;Test de Easyb&lt;/description&gt;
&lt;url&gt;http://www.chosesafaire.fr&lt;/url&gt;

&lt;build&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
            &lt;configuration&gt;
                &lt;source&gt;1.5&lt;/source&gt;
                &lt;target&gt;1.5&lt;/target&gt;
                &lt;encoding&gt;UTF-8&lt;/encoding&gt;
            &lt;/configuration&gt;
        &lt;/plugin&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.easyb&lt;/groupId&gt;
            &lt;artifactId&gt;maven-easyb-plugin&lt;/artifactId&gt;
            &lt;version&gt;0.9.6&lt;/version&gt;
            &lt;executions&gt;
                &lt;execution&gt;
                    &lt;goals&gt;
                        &lt;goal&gt;test&lt;/goal&gt;
                    &lt;/goals&gt;
                &lt;/execution&gt;
            &lt;/executions&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;junit&lt;/groupId&gt;
        &lt;artifactId&gt;junit&lt;/artifactId&gt;
        &lt;version&gt;4.7&lt;/version&gt;
        &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;/project&gt;
</pre><p>Nous développerons dans cet exemple une classe <strong>HelloMachine</strong>, qui n&#8217;a pour but que de renvoyer &laquo;&nbsp;Hello&nbsp;&raquo; et de stocker le fait d&#8217;avoir été appelée. Voici le scenario, à placer le fichier <strong>src/test/easby/HelloMachineStory.groovy</strong>, répertoire qui peut être facilement paramétré depuis le fichier Maven :</p><pre class="brush: groovy">
import fr.chosesafaire.HelloMachine

/**
 * Calling the HelloMachine !
 */
scenario "calling the hellomachine", {
    given "an hellomachine", {
        machine = new HelloMachine()
        hello = "Hello"
    }

    when "sayHello is called", {
        sayHello = machine.sayHello()
    }

    then "hello should be returned", {
        sayHello.shouldBe hello
    }

    and "then the machine should have been called", {
        machine.isCalled().shouldBe true
    }
}
</pre><p>Et enfin notre classe métier :</p><pre class="brush: java">
package fr.chosesafaire;

public class HelloMachine {
    private Boolean called = Boolean.FALSE;

    public String sayHello() {
        called = Boolean.TRUE;
        return "Hello";
    }

    public Boolean isCalled() {
        return called;
    }
}
</pre><p>Il ne reste qu&#8217;à lancer la compilation et les tests :</p><pre class="brush: shell">
mvn compile test
</pre><p>Si vous n&#8217;avez pas Groovy d&#8217;installé, Maven le fera pour vous, car c&#8217;est une dépendance d&#8217;Easyb. Le résultat dans la console devrait être le suivant (bien qu&#8217;il soit possible de générer des rapports HTML !) :</p><pre class="brush: shell">
[java] Running hello machine story (HelloMachineStory.groovy)
[java] Scenarios run: 1, Failures: 0, Pending: 0, Time Elapsed: 0.313 sec
[java] 1 behavior run with no failures
</pre><p>Ceci n&#8217;est qu&#8217;une brève introduction, j&#8217;espère que vous comprendrez l&#8217;intérêt d&#8217;un tel outil en étudiant plus en détail les fonctionnalités ! Voici une liste de liens utiles :</p><ul><li><a href="http://www.easyb.org/dsls.html">La documentation officielle</a></li><li><a href="http://www.easyb.org/maven-easyb-plugin/index.html">Maven Easyb Plugin</a></li><li><a href="http://code.google.com/p/easyb/w/list">Le wiki d&#8217;Easyb</a></li><li><a href="http://behaviour-driven.org/">Quelques détails concernant le Behavior Driven Development</a></li></ul><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/&amp;title=Easyb%2C+Behavior+Driven+Development" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/&amp;title=Easyb%2C+Behavior+Driven+Development" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/&amp;title=Easyb%2C+Behavior+Driven+Development" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/&amp;title=Easyb%2C+Behavior+Driven+Development" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/&amp;title=Easyb%2C+Behavior+Driven+Development" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=Easyb%2C+Behavior+Driven+Development+-+http://tinyurl.com/yhksbyf+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2009/10/vraptor-le-framework-qui-a-les-crocs/' rel='bookmark' title='Permanent Link: VRaptor, le framework qui a les crocs'>VRaptor, le framework qui a les crocs</a></li><li><a href='http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/' rel='bookmark' title='Permanent Link: Mocks et Multithreading'>Mocks et Multithreading</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>cachedLoad(), un plugin de cache pour jQuery</title><link>http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/</link> <comments>http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/#comments</comments> <pubDate>Tue, 26 Jan 2010 14:00:17 +0000</pubDate> <dc:creator>Pierre Quillery</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[cache]]></category> <category><![CDATA[jquery]]></category> <category><![CDATA[plugin]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=282</guid> <description><![CDATA[Aujourd&#8217;hui, pour changer un peu de notre habituel programme sur Ruby on Rails, je me propose de revenir sur un article que j&#8217;ai publié il y a quelques mois qui parlait des propriétés window.sessionStorage et window.localStorage présentes dans la spécification HTML 5. Nous allons exploiter la première pour améliorer de façon transparente les performances d&#8217;un [...]Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2009/09/file-get-contents-un-plugin-wordpress-pour-inclure-un-fichier-dans-vos-posts/' rel='bookmark' title='Permanent Link: file-get-contents : Un plugin Wordpress pour inclure un fichier dans vos posts'>file-get-contents : Un plugin Wordpress pour inclure un fichier dans vos posts</a></li><li><a href='http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/' rel='bookmark' title='Permanent Link: LocalStorage, la suite logique de Google Gears ?'>LocalStorage, la suite logique de Google Gears ?</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Aujourd&#8217;hui, pour changer un peu de notre habituel programme sur Ruby on Rails, je me propose de revenir sur un article que j&#8217;ai publié il y a quelques mois qui parlait des propriétés <code>window.sessionStorage</code> et <code>window.localStorage</code> présentes dans la spécification HTML 5. Nous allons exploiter la première pour améliorer de façon transparente les performances d&#8217;un site dans les navigateurs compatibles (<em>webkit</em> / <em>gecko</em> pour l&#8217;instant).</p><p><span id="more-282"></span></p><p><strong>L&#8217;idée est donc de créer un plugin pour jQuery qui va se substituer au chargement Ajax classique et stocker dans la mémoire du navigateur un bout de page pendant la durée de la session. </strong> Le site que j&#8217;ai équipé de ce système charge de cette façon 3 ou 4 éléments sur chaque page visitée (le pied de page, un bloc de connexion, un moteur de recherche, etc.). À l&#8217;instar des <em>cookies</em>, ce cache n&#8217;est accessible que sur un seul <em>vhost</em>.</p><p>Bien sûr, les navigateurs qui ne supportent pas cette propriété utiliseront un chargement classique, de la même façon qu&#8217;ils le faisaient auparavant. Concrètement, <strong>il suffit généralement de remplacer votre appel à la méthode <code>ajax</code> de jQuery pour bénéficier du cache</strong>.</p><p>Ainsi <code>$('#element').ajax('http://www.google.fr');</code> va devenir <code>$('#element').cachedLoad('http://www.google.fr');</code>. Vous pouvez également passer les paramètres habituels de la fonction <code>load()</code>.</p><p>Enfin, pour supprimer volontairement le cache associé à un div, il vous suffit d&#8217;appeler la méthode <code>cachedLoad()</code> sans aucun paramètre.</p><p>Voilà, j&#8217;espère que ce petit <em>plugin</em> vous sera utile <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> !</p><pre class="brush: javascript;">
/**
	cachedLoad 1.0
	Pierre Quillery
    http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/
 
	@param url string 
		Url à charger
	@param data object
		Paramètres à passer à la requête
	@param callback function 
		Fonction de callback à appeler éventuellement après le chargement
	
	Ce plugin permet d'utiliser la fonctionnalité html5 sessionStorage disponible 
	dans les navigateurs récents et ainsi éviter de multiplier les requêtes inutiles
	sur le serveur pour les bouts de page statiques (pied de page, formulaires de
	connexion etc.).
	
	*Si aucun paramètre n'est passé*, cela a pour effet de supprimer le cache
	associé à l'élément visé par le sélecteur.
	
	Voici la documentation &quot;mozilla&quot; :
	https://developer.mozilla.org/en/DOM/Storage#sessionStorage
	
	Pour les navigateurs n'implémentant pas cette fonctionnalité, on fait un 
	chargement classique.
	
	L'utilisation est calquée sur la méthode &quot;load()&quot; de jQuery
	(http://api.jquery.com/load/) : 
	
	$('#mon_div').cachedLoad('http://www.google.fr', function() { 
		alert('ok!'); 
	});
	
	$('#mon_div').cachedLoad('http://www.google.fr', {data:&quot;data&quot;}, function() { 
		alert('ok avec des paramètres!'); 
	});
	
	// Pour supprimer le cache
	$('#mon_div').cachedLoad();
	
	
	ATTENTION 
	==============================================================================
	Il est nécessaire que l'attribut ID du conteneur soit défini, car il est 
	utilisé pour définir une clé unique dans le tableau du sessionStorage. S'il 
	n'est pas	défini, on fera un appel Ajax classique et les données ne seront
	tout simplement pas mises en cache.
	==============================================================================
	
*/
jQuery.fn.cachedLoad = function(url, data, callback) {

	// Pour chaque appel du plugin ...
  return this.each(function() {
  	
  	// On récupère le contexte
		var self = $(this);
		var storage = window.sessionStorage; 

		// Est-ce qu'une URL a bien été passée en paramètre ?
		if(url) {
	
			// On autorise la syntaxe sans &quot;data&quot;, comme jQuery
			if(typeof(data) == 'function') { callback = data; data = {}; }
			// Options par défaut
			data 			= data 			|| null;
			callback 	= callback 	|| function(){};
	
			// Si le sessionStorage est disponible et que l'élément a bien un id ...
			if(storage &amp;&amp; self.attr('id') != '') {
		
				// On cherche le bout de page dans le cache
				var cache = storage['cachedLoad_' + self.attr('id')];

				// Le bout de page existait, on va l'afficher puis exécuter le callback
				if(cache) {
					self.html(cache);
					if(window.console &amp;&amp; console.log) 
						console.log(&quot;cachedLoad : &quot;, url);
					callback();
			
				// Le bout de page n'était pas en cache
				} else {
					// On le charge, on le met en cache, puis on appelle le callback
					self.load(url, data, function(r) {
						storage['cachedLoad_' + self.attr('id')] = r;
						callback();
					});
				}
	
			// Si le sessionStorage n'est pas disponible, on fait un appel classique
			// à la méthode load() de jQuery.
			} else {
				self.load(url, data, callback);
			}
		
		// Si aucune URL n'a été passée en paramètre, cela signifie que l'on
		// doit supprimer le cache
		} else {
		
			if(window.sessionStorage &amp;&amp; self.attr('id') != '') {
				// On supprime l'entrée du cache pour forcer le futur rechargement
				delete storage['cachedLoad_' + self.attr('id')];
				if(window.console &amp;&amp; console.log) 
					console.log(&quot;cachedLoad supprimé pour : &quot;, self.attr('id'));
			}
			
		}
			
	});
};

</pre><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/&amp;title=cachedLoad%28%29%2C+un+plugin+de+cache+pour+jQuery" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/&amp;title=cachedLoad%28%29%2C+un+plugin+de+cache+pour+jQuery" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/&amp;title=cachedLoad%28%29%2C+un+plugin+de+cache+pour+jQuery" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/&amp;title=cachedLoad%28%29%2C+un+plugin+de+cache+pour+jQuery" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/&amp;title=cachedLoad%28%29%2C+un+plugin+de+cache+pour+jQuery" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=cachedLoad%28%29%2C+un+plugin+de+cache+pour+jQuery+-+http://tinyurl.com/y9sqcps+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2009/09/file-get-contents-un-plugin-wordpress-pour-inclure-un-fichier-dans-vos-posts/' rel='bookmark' title='Permanent Link: file-get-contents : Un plugin Wordpress pour inclure un fichier dans vos posts'>file-get-contents : Un plugin Wordpress pour inclure un fichier dans vos posts</a></li><li><a href='http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/' rel='bookmark' title='Permanent Link: LocalStorage, la suite logique de Google Gears ?'>LocalStorage, la suite logique de Google Gears ?</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Mocks et Multithreading</title><link>http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/</link> <comments>http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/#comments</comments> <pubDate>Fri, 22 Jan 2010 12:45:09 +0000</pubDate> <dc:creator>Killian Ebel</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[java]]></category> <category><![CDATA[mock]]></category> <category><![CDATA[multithread]]></category> <category><![CDATA[tests]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=273</guid> <description><![CDATA[Afin de compléter la série d&#8217;articles sur les tests logiciels, voici une méthode permettant de tester efficacement une application &#171;&#160;concurrente&#160;&#187;.Java et Multithreading
Premièrement, il est important de comprendre le fonctionnement de l&#8217;interface Callable en Java ; une interface similaire à Runnable, à la différence près qu&#8217;un thread Callable renvoie une valeur lors de son exécution.
Ensuite, la [...]Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2009/10/mocks-stubs-la-suite/' rel='bookmark' title='Permanent Link: Mocks &amp; Stubs : La suite'>Mocks &amp; Stubs : La suite</a></li><li><a href='http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/' rel='bookmark' title='Permanent Link: Le problème du Singleton'>Le problème du Singleton</a></li><li><a href='http://www.chosesafaire.fr/2009/10/mocks-stubs-leurres-des-tests/' rel='bookmark' title='Permanent Link: Mocks &amp; Stubs : Leurres des Tests'>Mocks &amp; Stubs : Leurres des Tests</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Afin de compléter la série d&#8217;articles sur <a href="http://www.chosesafaire.fr/2009/10/mocks-stubs-leurres-des-tests/">les tests logiciels</a>, voici une méthode permettant de tester efficacement une application &laquo;&nbsp;concurrente&nbsp;&raquo;.</p><p><span id="more-273"></span></p><h2>Java et Multithreading</h2><p>Premièrement, il est important de comprendre le fonctionnement de l&#8217;interface <a href="http://kamleshkr.wordpress.com/2009/10/02/java-threads-callable-and-future/">Callable</a> en Java ; une interface similaire à <a href="http://java.sun.com/javase/6/docs/api/java/lang/Runnable.html">Runnable</a>, à la différence près qu&#8217;un thread <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/Callable.html">Callable</a> renvoie une valeur lors de son exécution.</p><p>Ensuite, la classe utilitaire <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/Executors.html">Executors</a>, utile pour créer des <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html">pool de Threads</a>, des <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/ScheduledExecutorService.html">pool de Threads périodique</a> ou encore transformer un <strong>Runnable</strong> en <strong>Callable</strong>.</p><p>Enfin, l&#8217;interface <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/Future.html">Future</a>, qui permet de stocker le futur résultat d&#8217;un appel asynchrone. Ce résultat peut être obtenu en appelant la méthode <strong>get()</strong> de votre objet, appel qui peut être bloquant si le résultat n&#8217;a pas encore été reçu. Il est possible de spécifier un <strong>timeout</strong> qui lèvera une exception si la durée de l&#8217;appel est supérieur.</p><p>Dans notre exemple, nous utiliserons un <a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29">pool de taille fixe</a> (nombre maximal de Threads défini), qui instanciera des <strong>Future</strong> stockant le résultat d&#8217;un calcul.</p><h2>Mocking et Multithreading</h2><p>C&#8217;est <a href="http://easymock.org/">EasyMock</a> qui sera utilisé, de par sa <a href="http://easymock.org/api/easymockclassextension/2.4/org/easymock/classextension/EasyMock.html#makeThreadSafe%28java.lang.Object,%20boolean%29">capacité</a> à rendre l&#8217;exécution d&#8217;un Mock <a href="http://fr.wikipedia.org/wiki/Threadsafe">Threadsafe</a>. <a href="http://mockito.org/">Mockito</a> permet aussi de créer des Mocks Threadsafe ; voici une brève comparaison entre <a href="http://mockito.org/">EasyMock et Mockito</a>, mais qui donne clairement l&#8217;avantage à Mockito du point de vue syntaxique !</p><h2>Mise en pratique</h2><p>Premièrement, la classe principale :</p><pre class="brush: java;">
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CalculMultiThread {
    private static final int THREAD_TIME_OUT = 10;
    private static final int POOL_SIZE = 5;

    private ExecutorService pool = null;

    public CalculMultiThread() {
        pool = Executors.newFixedThreadPool(POOL_SIZE);
    }

    public void setPool(ExecutorService pool) {
        this.pool = pool;
    }

    public void calculsParalleles(Integer op1, Integer op2, Integer op3, Integer op4) {
        final AdditionCallable additionCallable = new AdditionCallable();
        additionCallable.setOperande1(op1);
        additionCallable.setOperande2(op2);

        final SoustractionCallable soustractionCallable = new SoustractionCallable();
        soustractionCallable.setOperande1(op3);
        soustractionCallable.setOperande2(op4);

        final Future&lt;Integer&gt; additionFuture = pool.submit(additionCallable);
        final Future&lt;Integer&gt; soustractionFuture = pool.submit(soustractionCallable);

        try {
            final Integer resultatAddition = additionFuture.get(THREAD_TIME_OUT, TimeUnit.SECONDS);
            final Integer resultatSoustraction = soustractionFuture.get(THREAD_TIME_OUT, TimeUnit.SECONDS);

            System.out.println(op1 + " + " + op2 + " = " + resultatAddition);
            System.out.println(op3 + " + " + op4 + " = "  + resultatSoustraction);

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }

    public static void main(String[] args) {
        CalculMultiThread calculMultiThread = new CalculMultiThread();
        calculMultiThread.calculsParalleles(5, 6, 8, 3);
    }
}

abstract class CalculCallable implements Callable&lt;Integer&gt; {
    private Integer operande1 = null;
    private Integer operande2 = null;

    public void setOperande1(Integer operande1) {
        this.operande1 = operande1;
    }

    public Integer getOperande1() {
        return this.operande1;
    }

    public void setOperande2(Integer operande2) {
        this.operande2 = operande2;
    }

    public Integer getOperande2() {
        return this.operande2;
    }
}

class AdditionCallable extends CalculCallable {
	public Integer call() throws Exception {
		return getOperande1() + getOperande2();
	}
}

class SoustractionCallable extends CalculCallable {
	public Integer call() throws Exception {
		return getOperande1() - getOperande2();
	}
}
</pre><p>Le résultat de l&#8217;opération :</p><pre class="brush: shell;">
5 + 6 = 11
8 - 3 = 5
</pre><p>Enfin, les tests :</p><pre class="brush: java;">
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;

public class CalculMultiThreadTest {
    private ExecutorService executorServiceMock;
    private CalculMultiThread calculMultiThread;

    @Before
    public void setUp() throws Exception {
        executorServiceMock = EasyMock.createMock(ExecutorService.class);
        calculMultiThread = new CalculMultiThread();
        EasyMock.makeThreadSafe(executorServiceMock, true);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testCallTasksInParallel() throws Exception {
        calculMultiThread.setPool(executorServiceMock);

        Future&lt;Integer&gt; additionFutureMock = (Future&lt;Integer&gt;) EasyMock.createMock(Future.class);
        EasyMock.expect(additionFutureMock.get(EasyMock.anyLong(), EasyMock.isA(TimeUnit.class))).andReturn(11);
        EasyMock.replay(additionFutureMock);

        Future&lt;Integer&gt; soustractionFutureMock = (Future&lt;Integer&gt;) EasyMock.createMock(Future.class);
        EasyMock.expect(soustractionFutureMock.get(EasyMock.anyLong(), EasyMock.isA(TimeUnit.class))).andReturn(5);
        EasyMock.replay(soustractionFutureMock);

        EasyMock.expect(executorServiceMock.submit(EasyMock.isA(AdditionCallable.class))).andReturn(additionFutureMock);
        EasyMock.expect(executorServiceMock.submit(EasyMock.isA(SoustractionCallable.class))).andReturn(soustractionFutureMock);
        EasyMock.replay(executorServiceMock);

        calculMultiThread.calculsParalleles(5, 6, 8, 3);

        EasyMock.verify(additionFutureMock);
        EasyMock.verify(soustractionFutureMock);
        EasyMock.verify(executorServiceMock);
    }
}
</pre><p>Voilà, ce n&#8217;est pas plus compliqué de tester du code multithreadé. Pour plus d&#8217;informations, vous pouvez toujours consulter les liens qui suivent, merci !</p><ul><li><a href="http://merereflections.wordpress.com/2010/01/20/unit-testing-multi-threaded-code-with-easymock/">Source d&#8217;inspiration</a></li><li><a href="http://www.sizovpoint.com/2009/03/java-mock-frameworks-comparison.html">Comparaison des frameworks de Mock</a></li><li><a href="http://java.sun.com/docs/books/tutorial/essential/concurrency/">Java et applications concurrentes</a></li></ul><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/&amp;title=Mocks+et+Multithreading" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/&amp;title=Mocks+et+Multithreading" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/&amp;title=Mocks+et+Multithreading" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/&amp;title=Mocks+et+Multithreading" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/&amp;title=Mocks+et+Multithreading" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=Mocks+et+Multithreading+-+http://tinyurl.com/yjokfno+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2009/10/mocks-stubs-la-suite/' rel='bookmark' title='Permanent Link: Mocks &amp; Stubs : La suite'>Mocks &amp; Stubs : La suite</a></li><li><a href='http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/' rel='bookmark' title='Permanent Link: Le problème du Singleton'>Le problème du Singleton</a></li><li><a href='http://www.chosesafaire.fr/2009/10/mocks-stubs-leurres-des-tests/' rel='bookmark' title='Permanent Link: Mocks &amp; Stubs : Leurres des Tests'>Mocks &amp; Stubs : Leurres des Tests</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>La couche Modèle de Rails (Partie 3)</title><link>http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/</link> <comments>http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/#comments</comments> <pubDate>Sun, 10 Jan 2010 14:32:26 +0000</pubDate> <dc:creator>Pierre Quillery</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[modèle]]></category> <category><![CDATA[rails]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=265</guid> <description><![CDATA[Aujourd&#8217;hui, je me propose d&#8217;achever cette série par quelques explications concernant les fichiers de garniture (aussi appelés fixtures) ainsi que l&#8217;utilisation des filtres (on parle de named scope) pour faciliter la sélection de données.Cet article fait partie d&#8217;une série sur le framework Ruby on Rails dont vous pouvez consulter le plan sur mon ancien site.
Il [...]Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 2)'>La couche Modèle de Rails (Partie 2)</a></li><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 1)'>La couche Modèle de Rails (Partie 1)</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Aujourd&#8217;hui, je me propose d&#8217;achever cette série par quelques explications concernant les fichiers de garniture (aussi appelés <em>fixtures</em>) ainsi que l&#8217;utilisation des filtres (on parle de <em>named scope</em>) pour faciliter la sélection de données.</p><p><span id="more-265"></span></p><p>Cet article fait partie d&#8217;une série sur le framework <em>Ruby on Rails</em> dont vous pouvez <a href="http://dandelionmood.com/Ruby-on-Rails-lancez-vous.html">consulter le plan</a> sur mon ancien site.</p><p>Il est également le dernier de la série de trois articles que j&#8217;ai publié sur l&#8217;implémentation de la couche <strong>Modèle</strong> de <em>Ruby on Rails</em>. Vous pouvez lire <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/">le premier article</a> ou <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/">le second</a>, si vous ne l&#8217;avez pas fait.</p><h2>Les filtres (<em>named_scope</em>)</h2><p>Ces filtres sont à mon avis une fonctionnalité très intéressante de Rails : <strong>ils permettent d&#8217;ajouter simplement un jeu de critères nommé à une requête SQL</strong>. Si on traduit le terme Anglais, il s&#8217;agit littéralement de &laquo;&nbsp;points de vue nommés&nbsp;&raquo; ; ce n&#8217;est pas très explicite, et c&#8217;est pourquoi je préfère le terme de &laquo;&nbsp;filtre&nbsp;&raquo; &#8211; il ne faut cependant pas les confondre avec les filtres de Contrôleur <a href="http://dandelionmood.com/Ruby-on-Rails-Filtres-Flashes.html">dont je parlais dans un précédent article</a>.</p><p>Par exemple, imaginons que nous avons à notre disposition un magasin en ligne qui souhaite mettre en avant les produits qui sont les moins chers (dont le prix est entre 1 et 5 euros par exemple). Nous pourrions être tentés d&#8217;écrire ceci, par exemple :</p><pre class="brush: ruby;">
Articles.find :all, :conditions => { :prix => [ 0 .. 5 ] }
</pre><p>Notez la syntaxe <code>[ ... ]</code>, elle permet de spécifier un intervale de valeurs. L&#8217;inconvénient de cette façon de faire c&#8217;est que si par la suite nous voulons ajouter d&#8217;autres crtières de sélection (uniquement les articles de telle catégorie, uniquement ceux qui sont soldées, dont la date de péremption expire bientôt, etc.) nous allons devoir <strong>dupliquer ce critère</strong> &#8230; De plus, si notre client décide d&#8217;agrandir la gamme de prix, nous serions obligés de repasser sur tout notre code en risquant d&#8217;oublier quelque chose.</p><p>Nous pourrions bien sûr utiliser des constantes, ou des variables de classe, mais ça rendrait l&#8217;écriture de nos conditions encore plus longues et moins explicite &#8211; c&#8217;est là qu&#8217;interviennent les <code>named_scope</code>. Éditons la classe Article pour ajouter un filtre :</p><pre class="brush: ruby;">
class Article &lt; ActiveRecord::Base
  named_scode :pas_chers, :conditions => { :prix => [ 0 .. 5 ] }
end
</pre><p>Pour l&#8217;instant, nous n&#8217;avons fait que déplacer un peu de code du Contrôleur vers le Modèle, rien de transcendant &#8211; mais voilà ce que nous pouvons maintenant écrire :</p><pre class="brush: ruby;">
@articles = Article.pas_chers.find :all, :conditions => { :rubrique_id => 2 }
</pre><p><strong>Et voilà le travail : nous pouvons réutiliser et même chaîner des jeux de critères de sélection !</strong> Attention toutefois à ne pas en abuser, même si c&#8217;est assez tentant : cela a un impact sur les performances qui doit rester maîtrisé.</p><p>Pour en savoir plus, je vous renvoie à <a href="http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html#M002177">la documentation de la méthode sur l&#8217;API</a> ainsi qu&#8217;à <a href="http://railscasts.com/episodes/108-named-scope">un épisode des Railscasts de Ryan Bates</a> (en Anglais).</p><h2>Les fichiers de garniture (<em>fixtures</em>)</h2><p>Les fichiers de garniture permettent d&#8217;ajouter facilement des données de test à votre application et vont donc en faciliter le développement : leur écriture peut se faire sous forme de fichier <code>yaml</code> ou <code>csv</code>. La première forme est la plus intéressante, car elle s&#8217;intègre mieux à Rails et permet même d&#8217;utiliser directement les mécanismes de relations entre les classes Modèles <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/">que nous voyions dans un précédent article</a>.</p><p>Concrètement, tout se passe dans les fichiers qui sont générés automatiquement quand vous créez des Modèles à l&#8217;aide de la commande <code>script/generate model ...</code>. Regardez dans le répertoire <code>test/fixtures</code> pour les retrouver. En reprenant l&#8217;exemple des classes que nous voyions dans l&#8217;article précédent, vous devriez ainsi trouver les fichiers <code>articles.yml</code>, <code>taggables.yml</code>, <code>tags.yml</code> et <code>utilisateurs.yml</code>.</p><p>Ouvrons dans un premier temps le premier fichier, <code>articles.yml</code> :</p><pre class="brush: yaml;">
one:
  login: MyString
  mot_de_passe: MyString

two:
  login: MyString
  mot_de_passe: MyString
</pre><p>Comme vous pouvez le voir, le fait d&#8217;utiliser le générateur pour créer la classe de modèle Utilisateur a également eu pour effet de générer deux entrées dans ce fichier en renseignant directement les noms des colonnes que nous avions spécifié.</p><p>Notez également que chaque enregistrement possède déjà un identifiant (<code>one</code> et <code>two</code>) qui nous permettra d&#8217;y faire référence dans un autre fichier pour créer une relation !</p><p>Modifions un peu ce fichier pour créer des Utilisateur plus intéressants :</p><pre class="brush: yaml;">
pierre:
  login: pierre
  mot_de_passe: mdp1

killian:
  login: killian
  mot_de_passe: mdp2

georges:
  login: georges
  mot_de_passe: mdp3
</pre><p>Nous pouvons maintenant utiliser chacun de ces enregistrements dans la garniture de la classe Article ! Ouvrons donc le fichier <code>articles.yml</code> :</p><pre class="brush: yaml;">
one:
  titre: MyString
  contenu: MyText
  utilisateur_id: 1

two:
  titre: MyString
  contenu: MyText
  utilisateur_id: 1
</pre><p>Rails nous propose de changer la valeur de la colonne <code>utilisateur_id</code>, ce qui est tout à fait possible &#8211; cependant, puisque nous avons <em>déjà</em> défini des relations entre ces deux classes, nous allons pouvoir nous passer de cette étape et réécrire le contenu de ce fichier de la façon suivante :</p><pre class="brush: yaml;">
article1:
  titre: "Mon premier article"
  contenu: "Contenu de mon premier article"
  utilisateur: pierre

article2:
  titre: "Mon deuxième article"
  contenu: "Contenu de mon deuxième article"
  utilisateur: killian

article3:
  titre: "Mon troisième article"
  contenu: "Contenu de mon troisième article"
  utilisateur: pierre
</pre><p>Rails comprendra tout seul que l&#8217;utilisateur <code>pierre</code> auquel nous faisons référence a été défini dans le fichier <code>utilisateurs.yml</code>.</p><p>Nous pouvons maintenant vider notre base de développement (<code>rake db:reset</code>)pour la remplir avec ces quelques enregistrements, à l&#8217;aide de la commande <code>rake db:fixtures:load</code>. Ouvrez maintenant une console Rails, (<code>script/console</code>) et constatez que les enregistrements ont bien été créés dans la base :</p><pre class="brush: plain;">
>> Utilisateur.find :all
=> [#&lt;Utilisateur id: 134130964, login: "killian", mot_de_passe: "mdp2",
created_at: "2009-12-23 13:26:49", updated_at: "2009-12-23 13:26:49">,
#&lt;Utilisateur id: 219077049, login: "georges", mot_de_passe: "mdp3",
created_at: "2009-12-23 13:26:49", updated_at: "2009-12-23 13:26:49">,
#&lt;Utilisateur id: 362712307, login: "pierre", mot_de_passe: "mdp1",
created_at: "2009-12-23 13:26:49", updated_at: "2009-12-23 13:26:49">]

>> Article.find :all
=> [#&lt;Article id: 151089403, titre: "Mon troisième article",
contenu: "Contenu de mon troisième article", utilisateur_id: 362712307,
created_at: "2009-12-23 13:26:50", updated_at: "2009-12-23 13:26:50">,
#&lt;Article id: 655299028, titre: "Mon premier article",
contenu: "Contenu de mon premier article", utilisateur_id: 362712307,
created_at: "2009-12-23 13:26:50", updated_at: "2009-12-23 13:26:50">,
.#&lt;Article id: 1040597104, titre: "Mon deuxième article",
contenu: "Contenu de mon deuxième article", utilisateur_id: 134130964,
created_at: "2009-12-23 13:26:50", updated_at: "2009-12-23 13:26:50">]
</pre><p>Vous remarquerez que les identifiants des enregistrements créés par ce biais ne commencent jamais par 1, ce qui permet de les distinguer facilement des enregistrements standards et ce qui évitent qu&#8217;ils perturbent vos tests manuels.</p><p>Si vous voulez tagger un article dans les garnitures, c&#8217;est un peu plus sophistiqué, car il faut indiquer à Rails que vous passez par une table intermédiaire à cause de l&#8217;utilisation du système <code>has_many :through</code> ; la démarche reste cependant la même : voyez directement dans <a href="http://api.rubyonrails.org/classes/Fixtures.html">la documentation</a> pour en apprendre plus.</p><p>En guise de conclusion, notez que les <em>fixtures</em> ne sont pas une réponse à toutes les situations. Par exemple, c&#8217;est généralement une mauvaise idée de s&#8217;en servir pour placer des données nécessaires au bon fonctionnement de l&#8217;application : les données que vous y placerez doivent être réservées à vos tests fonctionnels &amp; unitaires.</p><p>Si vous avez besoin de mettre en place des données de base pour votre application en production, utilisez le fichier <code>db/seeds.rb</code>. Vous pourrez ensuite exécuter les commandes qu&#8217;il contient avec la commande <code>rake db:setup</code>.</p><h2>La suite au prochain épisode !</h2><p>Cet article était le dernier de notre série consacrée à la couche Modèle de Rails : aussi dès la semaine prochaine (si tout va bien <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ) nous entamerons une toute nouvelle séquence qui concernera l&#8217;implémentation des ressources et des formulaires dans notre <em>framework</em> préféré.</p><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+3%29" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+3%29" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+3%29" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+3%29" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+3%29" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=La+couche+Mod%C3%A8le+de+Rails+%28Partie+3%29+-+http://tinyurl.com/yggasbk+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 2)'>La couche Modèle de Rails (Partie 2)</a></li><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 1)'>La couche Modèle de Rails (Partie 1)</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>La couche Modèle de Rails (Partie 2)</title><link>http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/</link> <comments>http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/#comments</comments> <pubDate>Sat, 12 Dec 2009 21:38:02 +0000</pubDate> <dc:creator>Pierre Quillery</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[activerecord]]></category> <category><![CDATA[modèle]]></category> <category><![CDATA[rails]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=235</guid> <description><![CDATA[Aujourd'hui, nous expérimentons l'implémentation de la couche Modèle de Rails, à l'aide la console : au programme, la configuration d'une base de données MySQL, la découverte des opérations de CRUD basiques, ainsi que la mise en place de relations entre les objets métier.Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 3)'>La couche Modèle de Rails (Partie 3)</a></li><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 1)'>La couche Modèle de Rails (Partie 1)</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Voici la suite du premier article qui parlait de la couche Modèle dans Rails, n&#8217;hésitez pas à <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/">aller y jeter un œil</a> si vous ne l&#8217;avez pas encore lu ! Ce post fait partie d&#8217;une série de tutoriels dont vous pouvez trouver <a href="http://dandelionmood.com/Ruby-on-Rails-lancez-vous.html">la liste sur mon ancien site</a>.</p><p><span id="more-235"></span></p><h2>Configuration des base de données dans Rails</h2><p>Comme vous l&#8217;avez sans doute remarqué dans le précédent article, nous avons directement commencé à travailler avec la base de données sans pour autant avoir eu besoin de configurer quoi que ce soit au niveau de l&#8217;application. En effet, <a href="http://dandelionmood.com/Installation-de-Ruby-on-Rails-sous.html">ainsi que je le décrivais déjà ici</a>, Rails se repose par défaut sur une base Sqlite, ce qui permet de s&#8217;affranchir de configuration et rentrer directement dans le vif du sujet.</p><p>La configuration concernant les bases de données est placée dans le fichier <code>config/database.yml</code>. Comme vous pouvez le voir, on y trouve 3 zones qui correspondent à la configuration de chaque environnement (<code>dev</code>, <code>preprod</code> et <code>prod</code>). <strong>L&#8217;intérêt de ce fonctionnement est qu&#8217;il est très souple à l&#8217;usage : pour développer vous aurez rarement besoin d&#8217;une base MySql, alors qu&#8217;en production, c&#8217;est une nécessité.</strong></p><pre class="brush: yaml;">
# SQLite version 3.x
#   gem install sqlite3-ruby (not necessary on OS X Leopard)
development:
  adapter: sqlite3
  # Emplacement du fichier où se trouve la base
  database: db/development.sqlite3
  # Nombre de connexion dans le pool
  pool: 5
  timeout: 5000

# ...
</pre><p>Notons que MySQL n&#8217;est pas le seul <a href="http://wiki.rubyonrails.org/start">SGBD supporté par Rails</a> : il en est de même pour PostgreSQL, DB2, SQL Server, Oracle, Sybase, Firebird, etc.</p><p>Prenons l&#8217;exemple de MySQL, et changeons la configuration pour nous connecter à une base MySQL correspondant à notre application en production :</p><pre class="brush: yaml;">
# ...

production:
  adapter: mysql
  # Encodage du client
  encoding: utf8
  # Nom de la base à laquelle se connecter
  database: monapplication_prod
  pool: 5
  username: monnomdutilisateur
  password: monmotdepasse
  # Si la base se trouve sur la machine locale ...
  socket: /var/run/mysqld/mysql.sock
  # ... Ou si la base se trouve ailleurs
  host: serveurbdd.monapplication.com
</pre><h3>Interaction avec les bases de données avec la ligne de commande</h3><p>Nous avons vu dans le précédent article comment utiliser les migrations pour interagir avec les bases de données via la ligne de commande Rails. Voici quelques autres commandes qui vous permettront de tester tout ça à loisir :</p><ul><li><code>rake db:create</code> → Créer la base de donnée</li><li><code>rake db:destroy</code> → Détruire la base de données</li></ul><p>Notez que ces commandes, comme toutes les opérations en ligne de commande de Rails, sont sensibles au contexte pour le choix de l&#8217;environnement à affecter : par défaut, vous travaillez avec l&#8217;environnement de développement. Pour changer ce comportement, il vous faut changer la valeur de la variable d&#8217;environnement <code>RAILS_ENV</code> en lui donnant le nom de l&#8217;environnement de votre choix.</p><p>Ainsi vous écrirez par exemple la commande suivante pour supprimer le contenu de la base de production :</p><pre class="brush: bash;">
RAILS_ENV=production rake db:destroy
</pre><h2>Sélection, Modification et Suppression d&#8217;élément</h2><h3>Utilisation de la console Rails</h3><p>Ruby on Rails intègre une console interactive qui est un lieu d&#8217;expérimentation privilégié : vous pouvez directement créer des objets persistants en base, tester un bout de code, une fonction, etc. Nous allons l&#8217;utiliser pour la suite du tutorial, car cela nous évitera d&#8217;avoir à nous soucier des autres couches. Pour la démarrer, rien de plus simple :</p><pre class="brush: bash;">
script/console
</pre><p>Vous entrez maintenant dans la console interactive, tapez <code>quit</code> pour en sortir à tout moment ! Notez enfin que l&#8217;invite de commande commence toujours par les caractères <code>>></code> alors que les retours de ruby sont préfixés d&#8217;une flèche <code>=></code>.</p><h3>Mise en place de notre projet de travail</h3><p>Voici quelques commandes qui vous nous permettre de mettre en place un projet pour tester un peu les possibilités offertes en ce qui concerne les opérations simples de CRUD sur lesquelles nous allons nous attarder aujourd&#8217;hui.</p><pre class="brush: bash;">
rails appli_de_test
cd appli_de_test
script/generate model Utilisateur login:string mot_de_passe:string
script/generate model Article titre:string contenu:text utilisateur_id:integer
script/generate model Tag libelle:string --skip-timestamps
script/generate model Taggable tag_id:integer article_id:integer --skip-timestamps
</pre><p><strong>Nous allons créer, comme vous l&#8217;avez compris un blog minimaliste : un Utilisateur possède des Articles. Les Articles partagent un ou plusieurs Tags via la relation Taggable.</strong></p><p>Il ne nous reste plus qu&#8217;à éditer les fichiers de classes correspondants que vous trouverez dans le répertoire <code>app/models</code> pour exprimer explicitement les relations qui lient ces éléments entre eux :</p><pre class="brush: ruby;">
# app/models/utilisateur.rb
class Utilisateur &lt; ActiveRecord::Base
  has_many :articles
end

# app/models/article.rb
class Article &lt; ActiveRecord::Base
  belongs_to :utilisateur
  has_many :taggables
  has_many :tags, :through => :taggables
end

# app/models/taggable.rb
class Taggable &lt; ActiveRecord::Base
  belongs_to :article
  belongs_to :tag
end

# app/models/tag.rb
class Tag &lt; ActiveRecord::Base
  has_many :taggables
  has_many :articles, :through => :taggables

  # Ce champ est obligatoire, on force donc sa présence
  validates_presence_of :libelle
end
</pre><p>Maintenant, il ne nous reste plus qu&#8217;à mettre en place la structure de la base de données, à l&#8217;aide de la commande <code>rake db:migrate</code> puis à nous connecter à la console de Rails pour commencer nos essais : <code>script/console</code></p><h3>Création d&#8217;élément</h3><p>Précisons en guise de préambule que cet aperçu des possibilités qui vous sont offertes par Rails et Active Record (classe qui s&#8217;occupe spécifiquement de la persistence des données en base) est vraiment très superficiel ; je ne saurais trop vous conseiller de vous référer à <a href="http://rails.rubyonrails.org/classes/ActiveRecord/Base.html">la documentation d&#8217;Active Record</a> pour apprendre réellement à vous en servir <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p><p>Nous allons tout d&#8217;abord créer des Tags à l&#8217;aide de la méthode <code><a href="http://rails.rubyonrails.org/classes/ActiveRecord/Base.html#M002269">create()</a></code>. Cette méthode va instancier un objet Tag et créer directement l&#8217;enregistrement en base (si la validation s&#8217;est bien déroulée).</p><pre class="brush: ruby;">
>> t = Tag.create
=> #&lt;Tag id: nil, libelle: nil>
</pre><p>Ah, nous remarquons le Tag n&#8217;a pas d&#8217;id (<code>nil</code> signifie <code>null</code> en PHP) ; cela signifie qu&#8217;il n&#8217;est pas présent en base &#8211; or il devrait l&#8217;être, puisque c&#8217;est ce que nous attendons de la commande <code>create()</code> &#8230;</p><p>Une erreur s&#8217;est produite à cause de la validation que nous avons ajouté dans la classe Tag : j&#8217;ai volontairement omis de préciser le champ <code>libelle</code> et l&#8217;enregistrement n&#8217;a pu être effectué. C&#8217;est ce mécanisme qui nous permettra de faciliter la validation des formulaires, ainsi que nous le verrons dans un prochain article.</p><p>Rails nous indique en fait dans la variable <code>errors</code> de l&#8217;objet <code>t</code> la liste des erreurs qui se sont produites et qui empêchent la validation et donc l&#8217;enregistrement de l&#8217;élément :</p><pre class="brush: ruby;">
>> t = Tag.create
=> #&lt;Tag id: nil, libelle: nil>
>> t.errors
=> #&lt;ActiveRecord::Errors:0x7fd366f44020 @errors=#&lt;OrderedHash
  {"libelle"=>[#&lt;ActiveRecord::Error:0x7fd366f42f90 @attribute=:libelle,
  @type=:blank, @options={}, @message=:blank, @base=#&lt;Tag id: nil,
  libelle: nil>>]}>, @base=#&lt;Tag id: nil, libelle: nil>>
</pre><p>Le message n&#8217;est pas forcément très lisible présenté comme ça, mais nous apprenons ainsi qu&#8217;il s&#8217;agit d&#8217;une erreur qui concerne le champ <code>libelle</code> et qu&#8217;elle a le type <code>:blank</code>. Le mécanisme de validation de formulaire de Rails rendra ce tableau très facile à exploiter pour nous par la suite, même s&#8217;il n&#8217;a pas l&#8217;air très avenant vu sous cet angle <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> !</p><p>Essayons maintenant de reprendre la saisie des Tags en évitant de provoquer des erreurs :</p><pre class="brush: ruby;">
>> Tag.create :libelle => "Rock"
=> #&lt;Tag id: 1, libelle: "Rock">
>> Tag.create :libelle => "Folk"
=> #&lt;Tag id: 2, libelle: "Folk">
>> Tag.create :libelle => "Soul"
=> #&lt;Tag id: 3, libelle: "Soul">
>> Tag.create :libelle => "Funk"
=> #&lt;Tag id: 4, libelle: "Funk">
</pre><p><strong>Ce coup-ci, il est clair que tout se passe bien : les enregistrements se voient directement attribuer un id numérique, ce qui signifie qu&#8217;ils sont maintenant présents en base.</strong></p><h3>Sélection d&#8217;éléments</h3><p>Maintenant que la base est peuplée de quelques éléments, nous pouvons nous intéresser aux méthodes qui nous permettent de retrouver ces mêmes éléments. Tout passe à vrai dire par la méthode de classe <code>find</code> qui est disponible pour chaque classe de modèle.</p><p>Ainsi, pour sélectionner une collection d&#8217;éléments, nous utilisons la variante <code>find :all</code>, en voici un exemple :</p><pre class="brush: ruby;">
>> Tag.find :all
=> [#&lt;Tag id: 1, libelle: "Rock">, #&lt;Tag id: 2, libelle: "Folk">, #&lt;Tag id: 3, libelle: "Soul">, #&lt;Tag id: 4, libelle: "Funk">]
</pre><p>Notez que dans le retour qui est fait, Rails nous renvoie bien <strong>une collection d&#8217;éléments</strong> qu&#8217;il a placé dans un tableau  (<code>[ ... ]</code>) ; comme en Javascript, on emploie en Ruby les crochets pour écrire un tableau.</p><p>Essayons maintenant d&#8217;ajouter une condition à notre sélection &#8211; plusieurs syntaxes sont disponibles, voici celle qui permet de créer une sorte de requête préparée qui prend la forme d&#8217;un tableau. Les <code>?</code> dans la requête SQL sont ainsi remplacés par les valeurs entrées dans les index suivant. Ici nous recherchons par exemple les Tags qui commencent par la lettre F.</p><pre class="brush: ruby;">
>> Tag.find :all, :conditions => [ 'libelle like ?', 'F%' ]
=> [#&lt;Tag id: 2, libelle: "Folk">, #&lt;Tag id: 4, libelle: "Funk">]
</pre><p>Voyons maintenant comment sélectionner un élément unique dans la base ; pour cela, on emploie la version <code>find :first</code> de la méthode :</p><pre class="brush: ruby;">
>> Tag.find :first, 1
=> #&lt;Tag id: 1, libelle: "Rock">
</pre><p>Dans le cas présent, Rails a bien renvoyé un élément unique, et non un tableau, ainsi que c&#8217;était le cas précédemment. On aurait éventuellement pu omettre le <code>:first</code>, c&#8217;est en fait la méthode utilisée par Rails si on ne précise rien.</p><p>Nous allons maintenant voir une autre façon d&#8217;écrire les conditions : plutôt que de passer un tableau, il est également possible de passer un simple hash (<code>{ ... }</code>) &#8211; il s&#8217;agit d&#8217;une sorte de tableau associatif :</p><pre class="brush: ruby;">
>> Tag.find :first, :conditions => { :libelle => 'Rock' }
=> #&lt;Tag id: 1, libelle: "Rock">
</pre><p>Voilà les bases de ce qu&#8217;il est possible de faire pour sélectionner des éléments ; vous trouverez <a href="http://apidock.com/rails/ActiveRecord/Base/find/class ">ici le détail des différentes options</a> supportées par la méthode <code>find</code>, elles sont nombreuses. Vous pouvez également <a href="http://railscasts.com/episodes/15-fun-with-find-conditions">regarder ce screencast</a> qui fait une démonstration des possibilités offertes par cette méthode.</p><h3>Modification d&#8217;un élément</h3><p>Maintenant que vous savez retrouver un élément, il serait utile de savoir le modifier puis le sauvegarder ; voici comment faire en employant assez logiquement la méthode <code>save</code> :</p><pre class="brush: ruby;">
>> t = Tag.find :first, 1
=> #&lt;Tag id: 1, libelle: "Rock">
>> t.libelle = "Rock'n'Roll"
=> "Rock'n'Roll"
>> t.save
=> true
</pre><p>Je sélectionne ainsi l&#8217;élément &laquo;&nbsp;Rock&nbsp;&raquo;, que je désire modifier en &laquo;&nbsp;Rock&#8217;n'Roll&nbsp;&raquo;. Il me suffit de changer la valeur du champ &laquo;&nbsp;libelle&nbsp;&raquo;, puis de sauvegarder. Notez que la méthode <code>save</code> retourne une variable qui va nous permettre de savoir que l&#8217;enregistrement a bien été effectué, s&#8217;il a correctement passé la validation. <a href="http://apidock.com/rails/v2.3.4/ActiveRecord/Base/save">Retrouvez ici la documentation concernant cette méthode</a>.</p><p>Voici un exemple de modification qui va échouer :</p><pre class="brush: ruby;">
>> t = Tag.find 2
=> #&lt;Tag id: 2, libelle: "Folk">
>> t.libelle = ""
=> ""
>> t.save
=> false
>> t.errors
=> #&lt;ActiveRecord::Errors:0x7f9f6d187d60 @errors=#&lt;OrderedHash
  {"libelle"=>[#&lt;ActiveRecord::Error:0x7f9f6d185510 @attribute=:libelle,
  @type=:blank, @options={}, @message=:blank, @base=#&lt;Tag id: 2,
  libelle: "">>]}>, @base=#&lt;Tag id: 2, libelle: "">>
</pre><p>Comme avec la méthode <code>create</code> que nous voyions tout à l&#8217;heure, il est toujours possible d&#8217;accéder aux messages de validation. Il nous est toutefois possible de court-circuiter la validation si nous sommes dans un environnement contrôlé, ce qui permet d&#8217;accélérer le traitement, grâce à la méthode <code>save!</code>.</p><h3>Suppression d&#8217;un élément</h3><p>Dernière partie, et non la moins importante de notre passage en revue du CRUD le plus basique en Rails, la destruction d&#8217;un élément. Voilà comment s&#8217;y prendre, grâce à la méthode <code>destroy</code> :</p><pre class="brush: ruby;">
>> t = Tag.find :first, :conditions => {:id => 1}
=> #&lt;Tag id: 1, libelle: "Rock'n'Roll">
>> t.destroy
=> #&lt;Tag id: 1, libelle: "Rock'n'Roll">
>> Tag.find :first, :conditions => {:id => 1}
=> nil
</pre><p><strong>Il est important de noter que la fonction <code>destroy</code> retourne l&#8217;élément supprimé en cas de succès, ce qui s&#8217;avère souvent très utile.</strong></p><h2>Créons des relations entre les objets</h2><p>Maintenant que nous savons interagir avec nos Modèles, il nous reste à voir comment créer des relations entre eux ; nous allons ainsi créer un Utilisateur et lui ajouter plusieurs Articles, ce qui nous permettra de voir le fonctionnement de la relation 1 à plusieurs. Dans un second temps, nous verrons comment assigner des Tags à un Article donnée en passant par la relation plusieurs à plusieurs.</p><h3>Utiliser la relation Un à Plusieurs</h3><p>Voici quelques commandes maintenant connues qui vont nous permettre de créer quelques enregistrements que nous allons utiliser par la suite.</p><pre class="brush: ruby;">
>> util1 = Utilisateur.create :login => "utilisateur1", :mot_de_passe => "mdp1"
=> #&lt;Utilisateur id: 1, login: "utilisateur1", mot_de_passe: "mdp1",
  created_at: "2009-12-11 17:07:08", updated_at: "2009-12-11 17:07:08">
>> util2 = Utilisateur.create :login => "utilisateur2", :mot_de_passe => "mdp2"
=> #&lt;Utilisateur id: 2, login: "utilisateur2", mot_de_passe: "mdp2",
  created_at: "2009-12-11 17:07:21", updated_at: "2009-12-11 17:07:21">

>> arti1 = Article.create :titre => "Titre de mon premier article",
  :contenu => "Pas de contenu pour le moment"
=> #&lt;Article id: 1, titre: "Titre de mon premier article",
  contenu: "Pas de contenu pour le moment", utilisateur_id: nil,
  created_at: "2009-12-11 17:08:11", updated_at: "2009-12-11 17:08:11">
>> arti2 = Article.create :titre => "Titre de mon second article",
  :contenu => "Pas de contenu pour le moment"
=> #&lt;Article id: 2, titre: "Titre de mon second article",
  contenu: "Pas de contenu pour le moment", utilisateur_id: nil,
  created_at: "2009-12-11 17:08:28", updated_at: "2009-12-11 17:08:28">
>> arti3 = Article.create :titre => "Titre de mon troisième article",
  :contenu => "Pas de contenu pour le moment"
=> #&lt;Article id: 3, titre: "Titre de mon troisième article",
  contenu: "Pas de contenu pour le moment", utilisateur_id: nil,
  created_at: "2009-12-11 17:08:42", updated_at: "2009-12-11 17:08:42">
</pre><p>Comment changer le rédacteur du premier Article pour dire qu&#8217;il est en fait le premier Utilisateur ? Rien de plus simple :</p><pre class="brush: ruby;">
>> arti1.utilisateur = util1
=> #&lt;Utilisateur id: 1, login: "utilisateur1", mot_de_passe: "mdp1",
  created_at: "2009-12-11 17:07:08", updated_at: "2009-12-11 17:07:08">
>> arti1.save
=> true
>> arti1
=> #&lt;Article id: 1, titre: "Titre de mon premier article",
  contenu: "Pas de contenu pour le moment", utilisateur_id: 1,
  created_at: "2009-12-11 17:08:11", updated_at: "2009-12-11 17:10:41">
</pre><p>Si nous avions voulu exploiter la relation dans le sens inverse, il nous aurait fallu énoncer les choses différemment : en effet un Article n&#8217;a qu&#8217;un seul et unique Utilisateur dans notre exemple &#8211; <em>alors qu&#8217;un Utilisateur peut avoir écrit un nombre indéfini d&#8217;Articles</em>.</p><p>Rails nous donne donc accès à un tableau dans le lequel il nous suffit d&#8217;ajouter les Articles de notre choix à l&#8217;Utilisateur qui est leur auteur. Disons que le deuxième Utilisateur a écrit les deux derniers articles :</p><pre class="brush: ruby;">
>> util2.articles = [ arti2, arti3 ]
=> [#&lt;Article id: 2, titre: "Titre de mon second article",
  contenu: "Pas de contenu pour le moment", utilisateur_id: 2,
  created_at: "2009-12-11 17:08:28", updated_at: "2009-12-11 17:14:41">,
  #&lt;Article id: 3, titre: "Titre de mon troisième article",
  contenu: "Pas de contenu pour le moment", utilisateur_id: 2,
  created_at: "2009-12-11 17:08:42", updated_at: "2009-12-11 17:14:41">]
>> util2.save
=> true
>> util2
=> #&lt;Utilisateur id: 2, login: "utilisateur2", mot_de_passe: "mdp2",
  created_at: "2009-12-11 17:07:21", updated_at: "2009-12-11 17:07:21">
</pre><p><strong>Et voilà pour la relation Un à Plusieurs ; comme vous le voyez, le fonctionnement de Rails à ce sujet est assez intuitif et logique.</strong></p><h3>Utiliser la relation Plusieurs à Plusieurs</h3><p>Nous allons maintenant ajouter les tags que nous avons déjà employé précédemment pour tagguer nos Articles. Rails fait la plus grosse partie de notre travail grâce à la relation <code>has_many :through</code> que nous avons défini dans les classes Article et Tag : nous pouvons faire abstraction de la classe Taggable avec laquelle notre framework se chargera d&#8217;interagir.</p><p>Commençons par ajouter deux Tags à notre premier article :</p><pre class="brush: ruby;">
>> arti1.tags << Tag.find(:first, :conditions => { :libelle => "Folk" })
=> [#&lt;Tag id: 2, libelle: "Folk">]
>> arti1.tags << Tag.find(:first, :conditions => { :libelle => "Funk" })
=> [#&lt;Tag id: 2, libelle: "Folk">, #&lt;Tag id: 4, libelle: "Funk">]
>> arti1.tags
=> [#&lt;Tag id: 2, libelle: "Folk">, #&lt;Tag id: 4, libelle: "Funk">]
</pre><p>Notez que j&#8217;utilise une syntaxe différente pour interagir avec le tableau des tags de l&#8217;Article ; l&#8217;opérateur <code>&lt;&lt;</code> peut se lire comme : &laquo;&nbsp;ajoute l&#8217;élément suivant à ce tableau&nbsp;&raquo; ; il s&#8217;agit d&#8217;un équivalent du <code>array_push()</code> de PHP ou du <code>push()</code> de Javascript.</p><p>Ajoutons encore quelques Tags à un autre Article et regardons le contenu de la table contenant les Taggables, avec lesquels nous ne sommes pas directement intervenus, pour constater que <em>Rails a fait lui-même les associations nécessaires</em> pour nous permettre de nous abstraire de cette table de jointure.</p><pre class="brush: ruby;">
>> arti2.tags = Tag.find(:all)
=> [#&lt;Tag id: 2, libelle: "Folk">, #&lt;Tag id: 3, libelle: "Soul">,
  #&lt;Tag id: 4, libelle: "Funk">]
>> Taggable.find :all
=> [#&lt;Taggable id: 1, tag_id: 2, article_id: 1>,
  #&lt;Taggable id: 2, tag_id: 4, article_id: 1>,
  #&lt;Taggable id: 3, tag_id: 2, article_id: 2>,
  #&lt;Taggable id: 4, tag_id: 3, article_id: 2>,
  #&lt;Taggable id: 5, tag_id: 4, article_id: 2>]
</pre><p><strong>Voilà, c&#8217;est tout pour aujourd&#8217;hui !</strong> Dans quelques temps, nous verrons la suite (et <em>a priori</em> la fin) de notre programme initial sur la couche Modèle de Rails, avec les garnitures (<em>fixtures</em>) &amp; les filtres. En attendant, n&#8217;hésitez pas à essayer de refaire ce tutoriel chez vous, c&#8217;est tout son intérêt <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> , et laissez un commentaire si vous rencontrez un problème.</p><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+2%29" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+2%29" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+2%29" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+2%29" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+2%29" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=La+couche+Mod%C3%A8le+de+Rails+%28Partie+2%29+-+http://tinyurl.com/ye4x9h5+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 3)'>La couche Modèle de Rails (Partie 3)</a></li><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 1)'>La couche Modèle de Rails (Partie 1)</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>La couche Modèle de Rails (Partie 1)</title><link>http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/</link> <comments>http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/#comments</comments> <pubDate>Sat, 05 Dec 2009 14:51:54 +0000</pubDate> <dc:creator>Pierre Quillery</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[rails]]></category> <category><![CDATA[tutorial]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=229</guid> <description><![CDATA[Rails, en bon framework qu'il est, propose bien sûr une couche Modèle assez complète à laquelle les deux prochains articles se proposent de vous introduire. Nous couvrirons dans cet article la définition du concept de Modèle, la création d'objets à l'aide de Migrations, validerons les données et créerons des Relations entre ces même Modèles ... Copieux programme en perspective !Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 3)'>La couche Modèle de Rails (Partie 3)</a></li><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 2)'>La couche Modèle de Rails (Partie 2)</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Rails, en bon <em>framework</em> qu&#8217;il est, propose bien sûr une couche Modèle assez complète à laquelle les deux prochains articles se proposent de vous introduire. Nous couvrirons dans cet article <strong>la définition du concept de Modèle</strong>, la création d&#8217;objets à l&#8217;aide de <strong>Migrations</strong>, <strong>validerons les données</strong> et créerons des <strong>Relations</strong> entre ces même Modèles &#8230; Copieux programme en perspective !</p><p><span id="more-229"></span></p><p>Tout d&#8217;abord, un petit <em>disclaimer</em> s&#8217;impose par rapport au contenu de cet article : il s&#8217;agit de la suite d&#8217;une série d&#8217;articles que j&#8217;avais commencé sur mon précédent blog sans aller jusqu&#8217;au bout. Je ne pense pas reprendre les anciens articles sur ce blog-ci, ce qui n&#8217;aurait pas beaucoup de sens dans un premier temps &#8211; je vous invite donc à aller <a href="http://dandelionmood.com/Ruby-on-Rails-lancez-vous.html">consulter la page de présentation de mon projet</a> ainsi que les premiers articles sur l&#8217;ancien site.</p><h2>Un Modèle ?</h2><p>Le Modèle est, comme nous l&#8217;avons précédemment, une des trois couches du modèles MVC. Il s&#8217;agit de celle qui est en charge de permettre l&#8217;enregistrement et la recherche de données. Typiquement, on utilise pour cela une classe dite d&#8217;ORM, de mapping objet-relationnel, qui va nous permettre de nous abstraire d&#8217;un SGBD particulier tout en s&#8217;intégrant mieux dans un ensemble de classes.</p><p>L&#8217;idée est de réduire au maximum la distance qui sépare la base de données (l&#8217;implémentation du stockage de données) des objets métiers de notre application (un Client, un Article, un Blog, un Commentaire, etc.). C&#8217;est également dans la couche Modèle que nous allons définir facilement <strong>les relations</strong> entre ces derniers (un Article a plusieurs Commentaires, etc.) ainsi que la <strong>validation des données</strong> (la date de naissance d&#8217;un utilisateur donné doit toujours être sous la forme jj/mm/aaaa, etc.).</p><h2>Créons une nouvelle classe de Modèle à l&#8217;aide des Migrations</h2><p>Tout d&#8217;abord, qu&#8217;est ce qu&#8217;une migration ? Il s&#8217;agit d&#8217;un fichier qui va contenir la définition d&#8217;une ou plusieurs tables ainsi que des champs qui les composent. Ils peuvent être créés soit automatiquement, (lorsque vous ajoutez une classe métier) soit par un plugin &#8230; Vous pouvez également en générer à tout moment.</p><p>L&#8217;idée est de placer dans un fichier texte facile à lire et à éditer la structure de votre base de données. Rails s&#8217;occupera ensuite de générer les commandes SQL qui correspondent dans la base correspondant à votre environnement courant (nous verrons cela plus loin). <strong>Vous pouvez ainsi facilement versionner votre base.</strong></p><p>Créons donc un classe Modèle de test à l&#8217;aide de la console Rails. Comme vous le voyez, je précise immédiatement les champs que je connais de ce Modèle : ils seront ajoutés automatiquement par Rails dans le fichier de migration correspondant que nous complèterons manuellement tout à l&#8217;heure.</p><pre class="brush: bash;">
script/generate model Article titre:string date_publication:datetime contenu:text timestamps
</pre><p>Ça y est : quelques fichiers ont été créés, notamment <code>app/models/article.rb</code> qui est la classe de notre Modèle à propremment parler, et qui ne contient rien pour l&#8217;instant et <code>db/migrate/xxxx_create_article.rb</code>, notre Migration.</p><p>Voyons maintenant le contenu de ce deuxième fichier :</p><pre class="brush: ruby;">
class CreateArticles &lt; ActiveRecord::Migration

  # Méthode appelée pour mettre en place la migration
  def self.up
    create_table :articles do |t|
      t.string :titre
      t.text :contenu
      t.datetime :date_publication

      # Va créer un champ auteur_id que nous utiliserons un peu plus loin
      t.references :auteur

      # Ajout automatique de champs tels que created_at, modified_at, etc.
      # Ces champs seront ensuite gérés par Rails automatiquement.
      t.timestamps
    end
  end

  # Méthode permettant d'enlever la migration au besoin
  def self.down
    drop_table :articles
  end
end
</pre><p>Nous pouvons éditer le fichier pour ajouter éventuellement des champs supplémentaires puis déclencher la migration en utilisant la ligne de commande Rails. Nous pourrons alors utiliser le fichier de Modèle et poursuivre notre application.</p><pre class="brush: bash;">
rake db:migrate
</pre><h2>Validation dans le Modèle</h2><p>Nous allons maintenant travailler avec la classe Modèle qui se trouve dans le répertoire <code>app/models</code> et ajouter du code pour faciliter la validation des champs qui s&#8217;y trouvent. Cela signifie que nous pourrons ainsi nous assurer par exemple du bon formattage d&#8217;un email, d&#8217;une date de naissance, de la présence d&#8217;un titre etc.</p><p>Cette validation empêchera par défaut toute création d&#8217;enregistrement ou mise à jour. Elle ajoutera le cas échéant des messages au modèle (personnalisables et même disponibles en plusieurs langues via l&#8217;internationalisation du framework) que vous pourrez simplement montrer à l&#8217;utilisateur pour lui permettre de corriger facilement sa saisie.</p><pre class="brush: ruby;">
class Article &lt; ActiveRecord::Base
  # Le titre de l'article est obligatoire (il ne doit pas être vide)
  validate_presence_of :titre

  # Le titre ne devrait pas être trop court ...
  validates_length_of :titre, :minimum => 30

  # etc .
end
</pre><p>Voici une page où vous trouverez <a href="http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html">toutes les méthodes de validation à votre disposition</a>, les méthodes par défaut couvrent une bonne partie des besoins les plus évidents.</p><p>Nous verrons dans le prochain article comment exploiter efficacement ces messages de validation pour les afficher à l&#8217;internaute lors de sa saisie.</p><h2>Créons des Relations</h2><p>La création de relations est une partie intéressante des possibilités que Rails vous offre pour la couche Modèle : vous pouvez ainsi exprimer directement les principaux types de relations dont vous avez besoin pour reproduire tout schéma de base dans votre application.</p><p>Précisons d&#8217;abord que pour fonctionner, Rails a tout de même besoin que vous respectiez ses propres convention de nommage des champs pour lui permettre de retrouver ses petits et inférer ce que vous voulez faire. Ainsi, les champs de clés étrangères seront toujours nommées <em>champ_id</em>.</p><p>Je vais maintenant présenter les principaux types de relations disponibles, mais toutes ces méthodes sont très personnalisables, <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">voyez dans la documentation pour en apprendre plus à leur sujet</a>.</p><h3>Une à Plusieurs</h3><p>Un des cas le plus fréquent est la relation Une à Plusieurs : un Auteur a plusieurs Articles, un Article a plusieurs Commentaires, etc. Voici comment l&#8217;écrire dans les classes e Modèle (je pars du principe que les modèles correspondants ont été créés et migrés vers la base) :</p><pre class="brush: ruby;">
# Fichier app/models/commentaire.rb
class Commentaire &lt; ActiveRecord::Base
  # C'est cette table qui contient la clé étrangère article_id,
  # on écrit donc belongs_to puis le nom du Modèle à lier au singulier
  belongs_to :article
end

# Fichier app/models/article.rb
class Article &lt; ActiveRecord::Base
  # Deuxième côté de la relation, on écrit has_many puis le
  # nom du Modèle au pluriel.
  has_many :commentaires
end
</pre><h3>Une à Une</h3><p>Moins fréquent, vous pouvez restreindre cette relation pour créer une relation Une à Une en changeant le <code>has_many</code> par un <code>has_one</code>, comme dans cet exemple :</p><pre class="brush: ruby;">
# Fichier app/models/utilisateur.rb
class Utilisateur &lt; ActiveRecord::Base
  # C'est cette table qui contient la clé étrangère profil_id
  belongs_to :profil
end

# Fichier app/models/profil.rb
class Profil &lt; ActiveRecord::Base
  # Deuxième côté de la relation, on écrit has_many puis le
  # nom du Modèle au singulier.
  has_many :utilisateur
end
</pre><h3>Plusieurs à Plusieurs et Relations Composites</h3><p>Vous trouverez sur Internet de nombreux tutoriaux qui vous présenteront l&#8217;emploi de la technique dite du <em>habtm</em> ou <code>has_and_belongs_to</code> ; je ferais pour ma part l&#8217;impasse sur cette dernière qui est maintenant globalement dépréciée et qui ne devrait plus être utilisée dans la plupart des cas.</p><p>Voici donc la bonne façon de voir les choses pour créer une relation Plusieurs à Plusieurs viable, c&#8217;est à dire grâce au <code>has_many :through</code> :</p><pre class="brush: ruby;">
# Fichier app/models/utilisateur.rb
class Utilisateur &lt; ActiveRecord::Base
  has_many :abonnements
  has_many :magazines, :through => :abonnements
end

# Fichier app/models/abonnement.rb
class Abonnement &lt; ActiveRecord::Base
  # Notez qu'on emploie dans cet objet deux clés étrangères
  belongs_to :utilisateur
  belongs_to :magazine
end

class Magazine &lt; ActiveRecord::Base
  has_many :abonnements
  has_many :utilisateurs, :through => :abonnements
end
</pre><p>Rien ne vous empêche d&#8217;ajouter des champs supplémentaires à votre objet Abonnement si vous souhaitez enrichir la relation ; on pourrait imaginer enregistrer la durée de l&#8217;abonnement par exemple. Ce n&#8217;était pas possible avec l&#8217;ancienne technique (habtm), et c&#8217;est une des raisons pour laquelle elle tombe petit à petit en désuétude.</p><p>Vous pouvez, sur le même principe, créer des relations composites (ou polymorphiques) qui pourront vous servir à factoriser pas mal de code en créeant une interface commune auxquels des Modèles de différents types pourront s&#8217;accrocher. C&#8217;est un peu complexe à expliquer, mais un exemple vaut mieux qu&#8217;un long discours :</p><pre class="brush: ruby;">
class Coordonnee &lt; ActiveRecord::Base
  # Il faut deux champs supplémentaires dans la table coordonnees :
  # - localisable_id, un entier
  # - localisable_type, une chaîne
  # => Utilisez la syntaxe suivante dans la Migration :
  #    t.references :localisable, :polymorphic => true
  belongs_to :localisable, :polymorphic => true
end

class Utilisateur &lt; ActiveRecord::Base
  has_many :localisations, :as => :localisable
end

class Societe &lt; ActiveRecord::Base
  has_many :localisations, :as => :localisable
end
</pre><p>Grâce à cette syntaxe, les Utilisateurs et les Societes partagent la possibilité d&#8217;avoir un objet Coordonnees qui leur est lié, ce qui serait plus complexe à mettre en place sinon. Nous pourrions facilement faire évoluer ce modèle au besoin, c&#8217;est ce qui fait sa force.</p><p>Dans quelques jours, nous verrons la suite de notre programme sur les Modèles dans Rails en parlant des différents types de base de données disponibles et leur configuration, des requêtes préparées, etc. <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/">Voir l&#8217;article suivant !</a></p><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+1%29" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+1%29" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+1%29" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+1%29" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/&amp;title=La+couche+Mod%C3%A8le+de+Rails+%28Partie+1%29" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=La+couche+Mod%C3%A8le+de+Rails+%28Partie+1%29+-+http://tinyurl.com/y8cnaqz+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2010/01/la-couche-modele-de-rails-partie-3/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 3)'>La couche Modèle de Rails (Partie 3)</a></li><li><a href='http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-2/' rel='bookmark' title='Permanent Link: La couche Modèle de Rails (Partie 2)'>La couche Modèle de Rails (Partie 2)</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2009/12/la-couche-modele-de-rails-partie-1/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Le problème du Singleton</title><link>http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/</link> <comments>http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/#comments</comments> <pubDate>Wed, 02 Dec 2009 06:00:08 +0000</pubDate> <dc:creator>Killian Ebel</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[dependency injection]]></category> <category><![CDATA[ioc]]></category> <category><![CDATA[mock]]></category> <category><![CDATA[tests]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=214</guid> <description><![CDATA[Le concept du Singleton en lui-même n&#8217;est pas un souci, avoir une instance unique d&#8217;une classe est même souvent très pratique. Quand bien même, qu&#8217;en est-il de la testabilité d&#8217;un tel composant ?L&#8217;implémentation reconnue, le design pattern Singleton du fameux GoF, est malheureusement assez compliquée à tester : une classe ne devrait pas forcer elle-même [...]Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/' rel='bookmark' title='Permanent Link: Mocks et Multithreading'>Mocks et Multithreading</a></li><li><a href='http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/' rel='bookmark' title='Permanent Link: Easyb, Behavior Driven Development'>Easyb, Behavior Driven Development</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Le concept du Singleton en lui-même n&#8217;est pas un souci, avoir une instance unique d&#8217;une classe est même souvent très pratique. Quand bien même, qu&#8217;en est-il de la testabilité d&#8217;un tel composant ?<br /> <span id="more-214"></span><br /> L&#8217;implémentation reconnue, le <a href="http://fr.wikipedia.org/wiki/Singleton_%28patron_de_conception%29">design pattern Singleton</a> du fameux <a href="http://fr.wikipedia.org/wiki/Patron_de_conception#Liste_des_patrons_de_conception_du_GoF">GoF</a>, est malheureusement assez compliquée à tester : une classe ne devrait pas forcer <strong>elle-même</strong> son statut de Singleton, mais l&#8217;instance unique devrait être gérée au niveau de l&#8217;application.</p><h2>Le problème</h2><p>Imaginons un composant <strong>Service</strong>, une quelconque classe métier. Dans une de ses méthodes, la classe Service fait appel à un autre composant, nommé par exemple <strong>Counter</strong> ; ce dernier est un simple compteur qui incrémente sa valeur à chaque appel d&#8217;un service. Pour avoir un compteur commun à tous les services, il doit en exister une instance unique, partagée par tous les composants de type Service.</p><p>Ainsi, voici un exemple d&#8217;implémentation :</p><pre class="brush: java;">class Counter {
    private static final Counter instance = new Counter();
    private int count = 0;

    private Counter() {}

    public static Counter getInstance() {
        return instance;
    }

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}</pre><pre class="brush: java;">class Service {
    public void execute() {
        // ...
        Counter.getInstance().increment();
    }
}</pre><p>L&#8217;implémentation semble correcte, un test unitaire appelant le service peut être écrit afin d&#8217;en vérifier le comportement. Imaginons maintenant que :</p><ul><li>Un grand nombre d&#8217;autres services utilise ce compteur ;</li><li>L&#8217;appel à l&#8217;incrémentation du compteur soit très complexe, faisant appel à des webservices / se connectant à une base de données / lisant un fichier du système ;</li><li>Plusieurs tests unitaires, pouvant même être lancés simultanément, ont besoin d&#8217;un compteur &laquo;&nbsp;remis à zéro&nbsp;&raquo;.</li></ul><h2>Que faire ?</h2><p>Votre service est complètement dépendant de la classe Counter. Ecrire une méthode qui réinitialise le Singleton ? Rajouter des conditions dans la méthode du Singleton pour éviter les opérations trop importantes en cas de test ? Les tests unitaires n&#8217;ont pas à interférer votre code métier, qui réciproquement ne doit pas changer pour s&#8217;adapter aux tests. Ce qui importe dans ce test, c&#8217;est de vérifier le fonctionnement du service, et non l&#8217;appel au compteur ; comment <a href="http://www.chosesafaire.fr/2009/10/mocks-stubs-leurres-des-tests/">mocker</a> le compteur si celui-ci est un Singleton et crée lui-même son instance ?</p><p>Une solution au problème est l&#8217;injection de dépendances. Votre application (ou plutôt le <a href="http://fr.wikipedia.org/wiki/Inversion_de_contr%C3%B4le">container IoC</a>) va se charger d&#8217;instancier l&#8217;unique compteur et de l&#8217;injecter dans chacun des services. Le processus peut-être fait manuellement, mais sera bien plus simple à mettre en place avec un framework spécialisé comme <a href="http://www.springsource.org/">Spring</a> ou <a href="http://code.google.com/p/google-guice/">Guice</a>. Voici un exemple avec Spring :</p><pre class="brush: java;">interface Counter() {
    void increment();
    int getCount();
}</pre><pre class="brush: java;">class ComplexCounter implements Counter {
    private int count = 0;

    public Counter() {}

    public void increment() {
        // Beaucoup de choses compliquées et très lourdes, assez longues à exécuter (le temps d'aller boire un café)
        count++;
    }

    public int getCount() {
        return count;
    }
}</pre><pre class="brush: java;">class Service {
    private Counter counter;

    public Service(Counter counter) {
        this.counter = counter;
    }

    public void execute() {
        // ...
        counter.increment();
    }
}
</pre><pre class="brush: xml;">
&lt;bean id="counter" class="fr.chosesafaire.singleton.ComplexCounter" /&gt;
&lt;bean id="service"&gt;
    &lt;constructor-arg ref="counter" /&gt;
&lt;/bean&gt;
&lt;bean id="otherService"&gt;
     &lt;constructor-arg ref="counter" /&gt;
&lt;/bean&gt;
</pre><p>Ainsi votre instance de compteur reste un Singleton au sens conceptuel du terme (instance unique partagée par les autres composants), mais la classe Counter ne gère plus elle-même son instance. L&#8217;avantage est de pouvoir tester désormais le service en mockant le compteur, et éviter la liste d&#8217;ennuis énoncée précédemment (avec <a href="http://www.jmock.org/">jMock</a>) :</p><pre class="brush: java;">Mockery context = new Mockery();

public void testService() {
    final Counter counter = context.mock(Counter.class);
    context.checking(new Expectations() {{
        oneOf(counter).increment();
    }});

    Service service = new Service(counter);
    service.execute();

    // Assertions ...
    context.assertIsSatisfied();
}</pre><p>On s&#8217;attend seulement (<strong>Expectations</strong>) à ce que la méthode <strong>increment</strong> du compteur soit appelée, et peu importe son réel fonctionnement, vu que seul le Service doit être testé !</p><p><strong>Que pensez-vous de cette solution à propos du Singleton ? Comment faîtes-vous pour tester vos classes Singleton ?</strong></p><p>Voici quelques références :</p><ul><li><a href="http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf">Le guide de l&#8217;écriture d&#8217;un code testable</a>, par des développeurs de Google</li><li><a href="http://structuremap.sourceforge.net/SingletonInjection.htm">Injected Singleton</a></li></ul><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/&amp;title=Le+probl%C3%A8me+du+Singleton" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/&amp;title=Le+probl%C3%A8me+du+Singleton" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/&amp;title=Le+probl%C3%A8me+du+Singleton" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/&amp;title=Le+probl%C3%A8me+du+Singleton" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/&amp;title=Le+probl%C3%A8me+du+Singleton" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=Le+probl%C3%A8me+du+Singleton+-+http://tinyurl.com/yzbqcny+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2010/01/mocks-et-multithreading/' rel='bookmark' title='Permanent Link: Mocks et Multithreading'>Mocks et Multithreading</a></li><li><a href='http://www.chosesafaire.fr/2010/03/easyb-behavior-driven-development/' rel='bookmark' title='Permanent Link: Easyb, Behavior Driven Development'>Easyb, Behavior Driven Development</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2009/12/le-probleme-du-singleton/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Lucene, Scala et Spring</title><link>http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/</link> <comments>http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/#comments</comments> <pubDate>Thu, 19 Nov 2009 06:00:15 +0000</pubDate> <dc:creator>Killian Ebel</dc:creator> <category><![CDATA[Non classé]]></category> <category><![CDATA[Développement]]></category> <category><![CDATA[fonctionnel]]></category> <category><![CDATA[lucene]]></category> <category><![CDATA[maven]]></category> <category><![CDATA[scala]]></category> <category><![CDATA[spring]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=202</guid> <description><![CDATA[Apache Lucene est un moteur de recherche &#171;&#160;full-text&#160;&#187; à haute performance, écrit en Java, mais disponible pour d&#8217;autres plateformes comme Ruby, Perl, C++ et PHP. Utilisé par des applications critiques (Wikipedia pour ne citer qu&#8217;eux !), son efficacité n&#8217;est plus à prouver. Scala quant à lui est un langage moderne, statiquement typé, à mi-chemin entre [...]Aucun article lié]]></description> <content:encoded><![CDATA[<p><a href="http://lucene.apache.org/java/docs/">Apache Lucene</a> est un moteur de recherche &laquo;&nbsp;full-text&nbsp;&raquo; à haute performance, écrit en Java, mais disponible pour d&#8217;autres plateformes comme Ruby, Perl, C++ et PHP. Utilisé par des applications critiques (<a href="http://fr.wikipedia.org/wiki/Lucene">Wikipedia</a> pour ne citer qu&#8217;eux !), son efficacité n&#8217;est plus à prouver. <a href="http://www.scala-lang.org/">Scala</a> quant à lui est un langage moderne, statiquement typé, à mi-chemin entre l&#8217;orienté objet et le fonctionnel, qui compilé en bytecode Java et exécuté sur une JVM s&#8217;avère tout aussi performant que son grand frère.<br /> <span id="more-202"></span><br /> Il vous faudra premièrement télécharger <a href="http://demo.chosesafaire.fr/chosesafaire_lucene_scala_spring.zip">les sources du projet</a>.</p><p>L&#8217;objectif de ce tutoriel est d&#8217;implémenter un petit moteur de recherche Lucene en Scala, en joignant les deux bouts grâce à Spring. Etant débutant en Scala (j&#8217;étais jusqu&#8217;à présent plutôt du côté Groovy de la force), j&#8217;espère en avoir tiré tous les avantages dans cet exemple. Si certaines parties peuvent être écrites de façon plus fonctionnelle et moins Javaesque, n&#8217;hésitez pas à laisser un commentaire.</p><p>Les sources sont normalement assez commentées (en anglais) pour être compréhensibles ; il n&#8217;y a de toute façon que trois fichiers :</p><ul><li><strong>pom.xml</strong> qui est le fichier &laquo;&nbsp;projet&nbsp;&raquo; pour Maven ;</li><li><strong>applicationContext.xml</strong>, le fichier de configuration Spring ;</li><li><strong>Main.scala</strong>, le fichier où sont rassemblées toutes les sources pour simplifier la compréhension.</li></ul><h3>Compilation</h3><p>Pour compiler le projet, utilisez la commande :</p><pre class="brush: shell;">
mvn compile
</pre><p>Toutes les dépendances (JUnit, Spring, Lucene, Scala) seront téléchargées automatiquement si vous ne possédez par encore les bonnes versions.</p><h3>Execution</h3><p>Pour l&#8217;exécuter, vous pouvez utiliser :</p><pre class="brush: shell;">
mvn exec:java -Dexec.mainClass="fr.chosesafaire.lucene.Main"
</pre><p>Ou le lancer avec un argument (le mot clé recherché, lucene par défaut) :</p><pre class="brush: shell;">
mvn exec:java -Dexec.mainClass="fr.chosesafaire.lucene.Main" -Dexec.args="computer"
</pre><p>La sortie devrait ressembler à ceci :</p><pre class="brush: shell;">
Found 2 results.
Lucene in Action
Lucene for Dummies
</pre><p>Vous pouvez modifier le dictionnaire de données en ajoutant des éléments au bean <strong>dictionary</strong> dans le fichier <strong>applicationContext.xml</strong>. N&#8217;hésitez pas non plus à essayer les différentes classes de Lucene (changer le type de <strong>Directory</strong>, d&#8217;<strong>Analyzer</strong>&#8230;).</p><p>L&#8217;Analyzer mis en place (StandardAnalyzer) ne tient pas compte de la casse, vous pouvez donc chercher &laquo;&nbsp;lucene&nbsp;&raquo; ou &laquo;&nbsp;Lucene&nbsp;&raquo; et obtenir le même résultat.</p><p>Le Directory mis en place (RAMDirectory) va comme son nom l&#8217;indique stocker temporairement les données dans la mémoire ; bien d&#8217;autres types sont disponibles, tout a été résumé dans les commentaires du fichier <strong>applicationContext.xml</strong>.</p><p>Pour rappel, voici <a href="http://demo.chosesafaire.fr/chosesafaire_lucene_scala_spring.zip">les sources du projet</a>.</p><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/&amp;title=Lucene%2C+Scala+et+Spring" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/&amp;title=Lucene%2C+Scala+et+Spring" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/&amp;title=Lucene%2C+Scala+et+Spring" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/&amp;title=Lucene%2C+Scala+et+Spring" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/&amp;title=Lucene%2C+Scala+et+Spring" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=Lucene%2C+Scala+et+Spring+-+http://tinyurl.com/ya7xyz7+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Aucun article lié</p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2009/11/lucene-scala-et-spring/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>LocalStorage, la suite logique de Google Gears ?</title><link>http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/</link> <comments>http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/#comments</comments> <pubDate>Sat, 26 Sep 2009 06:00:45 +0000</pubDate> <dc:creator>Pierre Quillery</dc:creator> <category><![CDATA[Développement]]></category> <category><![CDATA[api]]></category> <category><![CDATA[firefox]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[jquery]]></category> <category><![CDATA[plugin]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=38</guid> <description><![CDATA[Cet article présente la réalisation d'une todo-list utilisant la propriété window.localStorage sous forme d'un plugin jQuery.Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/' rel='bookmark' title='Permanent Link: cachedLoad(), un plugin de cache pour jQuery'>cachedLoad(), un plugin de cache pour jQuery</a></li><li><a href='http://www.chosesafaire.fr/2009/10/mocks-stubs-la-suite/' rel='bookmark' title='Permanent Link: Mocks &amp; Stubs : La suite'>Mocks &amp; Stubs : La suite</a></li></ol>]]></description> <content:encoded><![CDATA[<p>Vous connaissez bien sûr Gears, le plugin de Google vous permettant de stocker de faire persister facilement des informations côté client accessible par une API Javascript. La principale application du système est de permettre <strong>un accès à l&#8217;information en mode déconnecté</strong> ou bien comme un <strong>mécanisme de cache</strong> pour votre application web.</p><p><span id="more-38"></span></p><div class="wp-caption alignright" style="width: 184px"><a href="http://www.flickr.com/photos/bob007/2907238255/"><img class="  " src="http://farm4.static.flickr.com/3069/2907238255_a99d1d6efb.jpg" alt="Photo de BOB08" width="174" height="210" /></a><p class="wp-caption-text">Photo Flickr de BOB008</p></div><p>Or, il existe aujourd&#8217;hui, incorporé directement dans Firefox 3.5, les versions les plus récentes de Webkit (Safari 4 notamment) et, cerise sur le gâteau, IE8, un moyen similaire de stocker des informations. Il s&#8217;agit de l&#8217;objet <strong>window.localStorage</strong> sur lequel on peut directement travailler pour faire persister des données : ce qu&#8217;on y stocke est accessible pour tout le domaine, même après fermeture du navigateur. Il existe également une version basée sur la session courante qui s&#8217;appelle assez logiquement <strong>window.sessionStorage</strong>.</p><p>Décidé à expérimenter le système par moi-même, j&#8217;ai donc décidé de <strong>créer une todo-list très simple</strong> sous forme d&#8217;un <em>plugin</em> pour jQuery. L&#8217;intérêt pratique est bien sûr relativement limité car les données ne sont pas sauvegardées sur un serveur et ne sont donc accessibles et persistantes que depuis un seul navigateur sur un unique poste.</p><p>Cela m&#8217;a cependant permis de me familiariser avec la création de <em>plugin</em> pour jQuery et la réalisation de la todo-list elle-même était amusante. Pour les opérations de manipulation de chaînes JSON, j&#8217;ai choisi de me reposer sur la librairie <a title="Le site officiel de jquery-json" href="http://code.google.com/p/jquery-json/" target="_blank"><em>jquery-json</em></a> plutôt que de réinventer la lune à ce niveau.</p><p>Pour les plus impatients, voilà <a title="Démo de la TodoList" href="http://demo.chosesafaire.fr/todoList/todoList.html" target="_blank">le lien pour voir la démo</a> &#8230; Attention à bien utiliser un navigateur compatible <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> !</p><p>Commençons par le plus évident, la page HTML qui va nous servir de base :</p><pre class="brush: html;">&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
  &lt;head&gt;
    &lt;title&gt;Une todo-list avec localStorage&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;

    &lt;script type=&quot;text/javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&quot;&gt;&lt;/script&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;http://jquery-json.googlecode.com/files/jquery.json-2.2.min.js&quot;&gt;&lt;/script&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;js/todolist.class.js&quot;&gt;&lt;/script&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;js/todolist.jquery.js&quot;&gt;&lt;/script&gt;

    &lt;link rel=&quot;Stylesheet&quot; href=&quot;css/styles.css&quot; /&gt;
    
    &lt;script type=&quot;text/javascript&quot;&gt;
      $(document).ready(function() {
        $('#conteneur-todolist').todoList();
      });
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Une todo-list avec localStorage&lt;/h1&gt;
    &lt;div id=&quot;conteneur-todolist&quot;&gt;&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre><p>J&#8217;ai ensuite choisi de créer deux fichiers distincts qui vont me permettre de bien séparer la couche métier de mon implémentation.<br /> Voilà tout d&#8217;abord la couche métier, la classe TodoList elle-même :</p><pre class="brush: javascript;">/**
  La classe Todolist va nous permettre de manipuler les tâches et des les 
  faire persister dans la mémoire du navigateur via window.localStorage.
  @param string identifiant 
    L'identifiant unique de la todoList
  @param function cb_mise_a_jour 
    Fonction de callback qui sera appelée à chaque mise à jour de la todoList.
*/
var TodoList = function(identifiant, cb_mise_a_jour) { 

  // Cet identifiant sera sa clé dans le tableau du localStorage
  this.identifiant = identifiant;

  // On récupère les données qui existaient déjà, 
  // ou on utilise les données par défaut.
  this.donnees = $.evalJSON(window.localStorage.getItem(this.identifiant)) || {
    taches : [ 
      {
        'libelle': &quot;Libellé de la première tâche&quot;,
        'effectue': false
      },
      {
        'libelle': &quot;Libellé de la deuxième tâche&quot;,
        'effectue': false
      },
      {
        'libelle': &quot;Libellé de la troisième tâche ... Elle est finie&quot;,
        'effectue': true
      }
    ]
  };

  // L'utilisateur devrait implémenter la méthode de callback pour 
  // rafraîchir son interface à chaque modification de la todolist.
  this.cb_mise_a_jour = cb_mise_a_jour || function() {
    throw 'Vous devriez implémenter la méthode cb_mise_a_jour(instance)';
  }
  
  // On exécute une première fois la méthode pour initialiser l'interface.
  this.cb_mise_a_jour(this);
}


/**
  Retourner toutes les tâches
*/
TodoList.prototype.lister_taches = function() {
  return this.donnees.taches;
}


/**
  Ajouter une nouvelle tâche
  @param string libelle_tache Libellé de la tâche a ajouter. 
*/
TodoList.prototype.ajout_tache = function(libelle_tache) {
  this.donnees.taches.push({
    'libelle': libelle_tache,
    'effectue': false
  });
  this.sauvegarder();
}


/**
  Modifier une tâche
  @param int numero Numéro de la tâche à modifier.
  @param object infos_nouvelle_tache La tâche après mise à jour
*/
TodoList.prototype.modification_tache = function(numero, infos_nouvelle_tache) {
  jQuery.extend(this.donnees.taches[numero], infos_nouvelle_tache);
  this.sauvegarder();
}


/**
  Supprimer une tâche
  @param int numero Numéro de la tâche à supprimer.
*/
TodoList.prototype.suppression_tache = function(numero) {
  this.donnees.taches.splice(numero, 1);
  this.sauvegarder();
}


/**
  Faire persister la TodoList dans le localStorage
*/
TodoList.prototype.sauvegarder = function() {
  window.localStorage.setItem(this.identifiant, $.toJSON(this.donnees));
  this.cb_mise_a_jour(this);
}
</pre><p>Voilà maintenant le plugin jQuery à proprement parler :</p><pre class="brush: javascript;">/**
  TodoList - Plugin pour jQuery
  Utilisation : $(selecteur_div).todoList();
*/
(function($){
  $.fn.todoList = function(options) {
    
    // On peut utiliser plusieurs TodoLists sur la page
    return this.each(function() {

      // Définition des éléments de l'interface
      var liste_taches = $('&lt;ul&gt;&lt;/ul&gt;');
      var champ_texte = $('&lt;input type=&quot;text&quot; /&gt;');
      var bouton_ajout = $('&lt;input type=&quot;button&quot; value=&quot;Ajouter&quot; /&gt;');
      $(this).append(liste_taches, champ_texte, bouton_ajout);
      $(this).attr('class','todolist');
      
      // Action permettant d'ajouter une nouvelle tâche à accomplir
      var ajout_tache = function() {
        var contenu_texte = $(champ_texte).val();
        if(contenu_texte != '') {
          // On crée une nouvelle entrée dans le tableau des tâches.
          TDL.ajout_tache(contenu_texte);
        }
      }
      
      // Ajout de l'écouteur du bouton.
      $(bouton_ajout).click(ajout_tache);
      // Ajout de l'écouteur du champ textuel
      $(champ_texte).keyup(function(e) {
        if(e.keyCode == 13) ajout_tache();
      });
      
      
      // Implémentation de la méthode de callback de la TodoList
      var cb_mise_a_jour = function(TDL) {
        // On vide la liste des tâches et le champ texte
        $(liste_taches).html('');
        $(champ_texte).val('');
        
        // Pour chaque tâche ...
        $.each(TDL.lister_taches(), function(i,tache) {
        
          // On génère un identifiant pour chaque tâche
          tache.identifiant = TDL.identifiant + '_tache_' + i;
          
          // On ajoute la tâche à la liste        
          $(liste_taches).append(
            $('&lt;li&gt;&lt;/li&gt;')
              .attr('class',(tache.effectue)?'tache_effectuee':'')
              .append(
                $('&lt;input type=&quot;checkbox&quot; /&gt;')
                  .attr('id', tache.identifiant)
                  .attr('checked', tache.effectue)
                  .click(function() {
                    // Un clic sur la checkbox permet de changer le statut
                    // de la tâche visée.
                    TDL.modification_tache(i, {
                      'effectue': $(this).attr('checked')
                    });
                  })
                ,
                
                $('&lt;label&gt;&lt;/label&gt;')
                  .attr('for', tache.identifiant)
                  .html(tache.libelle)
                ,
                
                ' '
                ,
                
                // On ajoute la possibilité de supprimer la tâche
                $('&lt;a&gt;&lt;/a&gt;')
                  .attr('href', 'javascript:void(0)')
                  .attr('title', 'Cliquez pour supprimer la tâche')
                  .click(function() {
                    $(this).parent().fadeOut(function() {
                      TDL.suppression_tache(i);
                    });
                  })
                  .html('x')
              )
          );
        });
      };
      
      
      
      // Instanciation de l'objet TodoList
      try {
        var TDL = new TodoList('todolist_'+$(this).attr('id'), cb_mise_a_jour);
      } 
      catch(e) {
        // En cas d'erreur on avertit l'utilisateur.
        alert(&quot;Une erreur s'est produite, il est probable que votre navigateur&quot;+
          &quot; ne supporte pas encore la propriété window.localStorage \n\n&quot; +
          &quot;Message retourné : \n&quot; + e);
      }
      
      
    });
  };
})(jQuery);
</pre><p>Et voilà, quelques styles CSS et on est au bout. En conclusion, je dirais que ce système est assez intéressant surtout pour servir éventuellement de fourre-tout, si votre application a une masse importante de données relativement statiques à transmettre au client. On pourrait imaginer dériver de cette TodoList pour créer un plugin généraliste pour jQuery qui permettait d&#8217;utiliser le localStorage s&#8217;il est disponible &#8211; et sinon se rabattre sur une simple variable globale &#8230; J&#8217;y réfléchis en ce moment, cela fera sans doute l&#8217;objet d&#8217;un post ultérieur <img src='http://www.chosesafaire.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/&amp;title=LocalStorage%2C+la+suite+logique+de+Google+Gears+%3F" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/&amp;title=LocalStorage%2C+la+suite+logique+de+Google+Gears+%3F" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/&amp;title=LocalStorage%2C+la+suite+logique+de+Google+Gears+%3F" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/&amp;title=LocalStorage%2C+la+suite+logique+de+Google+Gears+%3F" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/&amp;title=LocalStorage%2C+la+suite+logique+de+Google+Gears+%3F" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=LocalStorage%2C+la+suite+logique+de+Google+Gears+%3F+-+http://tinyurl.com/yb2mq6s+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2010/01/cachedload-un-plugin-de-cache-pour-jquery/' rel='bookmark' title='Permanent Link: cachedLoad(), un plugin de cache pour jQuery'>cachedLoad(), un plugin de cache pour jQuery</a></li><li><a href='http://www.chosesafaire.fr/2009/10/mocks-stubs-la-suite/' rel='bookmark' title='Permanent Link: Mocks &amp; Stubs : La suite'>Mocks &amp; Stubs : La suite</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2009/09/localstorage-la-suite-logique-de-google-gears/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Adminer, un outil léger d&#8217;administration de base MySQL</title><link>http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/</link> <comments>http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/#comments</comments> <pubDate>Wed, 23 Sep 2009 06:00:59 +0000</pubDate> <dc:creator>Pierre Quillery</dc:creator> <category><![CDATA[Administration serveur]]></category> <category><![CDATA[administration]]></category> <category><![CDATA[application]]></category> <category><![CDATA[bdd]]></category> <category><![CDATA[Développement]]></category> <category><![CDATA[mysql]]></category> <category><![CDATA[web]]></category><guid isPermaLink="false">http://www.chosesafaire.fr/?p=17</guid> <description><![CDATA[Adminer est une alternative simple et efficace au classique PhpMyAdmin.Articles liés :<ol><li><a href='http://www.chosesafaire.fr/2009/10/tiny-tiny-rss-une-alternative-a-google-reader/' rel='bookmark' title='Permanent Link: Tiny Tiny RSS : une alternative à Google Reader'>Tiny Tiny RSS : une alternative à Google Reader</a></li></ol>]]></description> <content:encoded><![CDATA[<p style="text-align: center;"><img class="size-full wp-image-23 aligncenter" title="Logo d'Adminer" src="http://www.chosesafaire.fr/wp-content/uploads/2009/09/logo.gif" alt="Logo d'Adminer" width="280" height="64" /></p><p>Si vos besoins en administration de bases de données ne demandent pas une interface très poussée, pourquoi ne pas jeter un œil du côté d&#8217;<a title="Le site officiel d'Adminer" href="http://www.adminer.org">Adminer</a> ? Ce petit logiciel se présente comme une alternative au classique <em>PhpMyAdmin</em> : plus simple, plus sobre et plus facile à installer, il consiste en un seul et unique fichier PHP à déposer sur votre serveur dédié. Si l&#8217;apparence d&#8217;origine ne vous convient pas, vous avez même la possibilité d&#8217;utiliser une des feuilles de styles proposées sur le site pour le personnaliser à votre guise.</p><p><span id="more-17"></span></p><p>Son interface est mieux conçue à mon sens que celle de <em>PhpMyAdmin</em>, <strong>plus simple et plus ergonomique</strong> ; ses fonctionnalités s&#8217;approchent de son grand frère sur de nombreux points, le <em>dépassent</em> même parfois  :</p><ul><li>Un assistant de construction de requête <strong>SELECT</strong> intéressant dans son fonctionnement et mieux conçu que celui de <em>PhpMyAdmin</em> ; il permet de gagner un temps précieux en vous permettant de rester très proche de la construction d&#8217;une requête SQL classique.</li><li>Fonctionnalités basiques d&#8217;<strong>exportation</strong> : elle est disponible uniquement au format SQL et CSV, ce qui devrait suffire dans la majeure partie des cas, mais <em>PhpMyAdmin</em> reste plus complet sur ce point.</li><li>Possibilité d&#8217;<strong>afficher le schéma de la base</strong> sélectionnée sous la forme de multiples divs colorés avec les noms des champs. J&#8217;ignore si cette fonctionnalité existe également sous <em>PhpMyAdmin</em>, mais je trouve que c&#8217;est un plus sympathique pour ce petit outil.</li><li><strong>Insertion de données</strong> dans une table depuis un fichier CSV de votre choix, ça peut toujours servir.</li><li>Plus classiquement, il est au même niveau que <em>PhpMyAdmin</em> pour :<ul><li>Exécution d&#8217;une requête arbitraire directement depuis un champ de texte ou un fichier sur votre disque dur.</li><li>Analyse, Optimisation, Purge de TABLE.</li><li>Création facile de TABLES, d&#8217;INDEX et de TRIGGER.</li></ul></li></ul><p><strong>On a donc affaire à un outil qui se fixe un projet clair, ambitieux et qui remplit parfaitement le rôle qu&#8217;il s&#8217;est fixé, une belle réussite à mon sens</strong> &#8211; il a remplacé chez moi <em>PhpMyAdmin</em>, car je ne me servais de toutes façons pas des fonctionnalités qui rendent ce dernier véritablement intéressant. <strong>Il nécessite par contre une bonne compréhension du langage SQL</strong>, ce qui le rend du coup un peu moins accessible que ne l&#8217;est <em>PhpMyAdmin</em>.</p><table border="0"><tbody><tr><td><p><div id="attachment_22" class="wp-caption alignnone" style="width: 160px"><a rel="lightbox" href="http://www.chosesafaire.fr/wp-content/uploads/2009/09/adminer_select.png"><img class="size-thumbnail wp-image-22  " title="Construire un SELECT" src="http://www.chosesafaire.fr/wp-content/uploads/2009/09/adminer_select-150x150.png" alt="Construire un SELECT avec Adminer" width="150" height="150" /></a><p class="wp-caption-text">Construire un SELECT</p></div></td><td><p><div id="attachment_20" class="wp-caption alignnone" style="width: 160px"><a rel="lightbox" href="http://www.chosesafaire.fr/wp-content/uploads/2009/09/adminer.png"><img class="size-thumbnail wp-image-20 " title="L'interface d'Adminer" src="http://www.chosesafaire.fr/wp-content/uploads/2009/09/adminer-150x150.png" alt="L'interface d'Adminer" width="150" height="150" /></a><p class="wp-caption-text">L&#39;interface d&#39;Adminer</p></div></td><td><p><div id="attachment_21" class="wp-caption alignnone" style="width: 160px"><a rel="lightbox" href="http://www.chosesafaire.fr/wp-content/uploads/2009/09/adminer_schema.png"><img class="size-thumbnail wp-image-21  " title="Générez un schéma de votre base !" src="http://www.chosesafaire.fr/wp-content/uploads/2009/09/adminer_schema-150x150.png" alt="Générez un schéma de votre base !" width="150" height="150" /></a><p class="wp-caption-text">Générez un schéma de votre base !</p></div></td></tr></tbody></table><p><strong><br /> </strong></p><div class="sexy-bookmarks sexy-bookmarks-center"><ul class="socials"><li class="sexy-blogmarks"> <a href="http://blogmarks.net/my/new.php?mini=1&amp;simple=1&amp;url=http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/&amp;title=Adminer%2C+un+outil+l%C3%A9ger+d%27administration+de+base+MySQL" rel="nofollow" title="Marquez-le sur BlogMarks">Marquez-le sur BlogMarks</a></li><li class="sexy-google"> <a href="http://www.google.com/bookmarks/mark?op=add&amp;bkmk=http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/&amp;title=Adminer%2C+un+outil+l%C3%A9ger+d%27administration+de+base+MySQL" rel="nofollow" title="Ajoutez-le à Google Bookmarks">Ajoutez-le à Google Bookmarks</a></li><li class="sexy-delicious"> <a href="http://del.icio.us/post?url=http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/&amp;title=Adminer%2C+un+outil+l%C3%A9ger+d%27administration+de+base+MySQL" rel="nofollow" title="Partagez-le sur del.icio.us">Partagez-le sur del.icio.us</a></li><li class="sexy-reddit"> <a href="http://reddit.com/submit?url=http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/&amp;title=Adminer%2C+un+outil+l%C3%A9ger+d%27administration+de+base+MySQL" rel="nofollow" title="Partagez-le sur Reddit">Partagez-le sur Reddit</a></li><li class="sexy-stumbleupon"> <a href="http://www.stumbleupon.com/submit?url=http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/&amp;title=Adminer%2C+un+outil+l%C3%A9ger+d%27administration+de+base+MySQL" rel="nofollow" title="Tomber sur un bon truc ? Partagez cet article sur StumbleUpon">Tomber sur un bon truc ? Partagez cet article sur StumbleUpon</a></li><li class="sexy-technorati"> <a href="http://technorati.com/faves?add=http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/" rel="nofollow" title="Partagez-le sur Technorati">Partagez-le sur Technorati</a></li><li class="sexy-twitter"> <a href="http://twitter.com/home?status=Adminer%2C+un+outil+l%C3%A9ger+d%27administration+de+base+MySQL+-+http://tinyurl.com/mzl4ka+(via+@dandelionmood)" rel="nofollow" title="Tweetez-le !">Tweetez-le !</a></li><li class="sexy-comfeed"> <a href="http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/feed" rel="nofollow" title="S'abonner aux commentaires de cet article ?">S'abonner aux commentaires de cet article ?</a></li></ul><div style="clear:both;"></div></div><p>Articles liés :</p><ol><li><a href='http://www.chosesafaire.fr/2009/10/tiny-tiny-rss-une-alternative-a-google-reader/' rel='bookmark' title='Permanent Link: Tiny Tiny RSS : une alternative à Google Reader'>Tiny Tiny RSS : une alternative à Google Reader</a></li></ol></p>]]></content:encoded> <wfw:commentRss>http://www.chosesafaire.fr/2009/09/adminer-un-outil-leger-dadministration-de-base-mysql/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> </channel> </rss>