<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns="http://purl.org/rss/1.0/">

<channel rdf:about="http://www.cheminsdetraverse.info/index.php">
  <title>Les chemins de traverse</title>
  <description><![CDATA[ic, on parle de nouvelles technologies du web, de PHP, de java, de XHTML, de CSS, d'Ajax, etc...]]></description>
  <link>http://www.cheminsdetraverse.info/index.php</link>
  <dc:language>fr</dc:language>
  <dc:creator></dc:creator>
  <dc:rights></dc:rights>
  <dc:date>2008-01-05T13:30:40+01:00</dc:date>
  <admin:generatorAgent rdf:resource="http://www.dotclear.net/" />
  
  <sy:updatePeriod>daily</sy:updatePeriod>
  <sy:updateFrequency>1</sy:updateFrequency>
  <sy:updateBase>2008-01-05T13:30:40+01:00</sy:updateBase>
  
  <items>
  <rdf:Seq>
    <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2008/01/05/38-creer-une-boutique-en-ligne-partie-4-vue-compacte-et-multilinguisme" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/12/31/37-creer-une-boutique-en-ligne-avec-rails-partie-3-la-boutique" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/12/31/36-creer-une-boutique-en-ligne-avec-rails-partie-2-la-gestion-des-produits" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/12/18/35-creer-une-boutique-en-ligne-partie-1-choix-des-outils" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/09/05/34-extensions-firefox-pour-developper-vos-sites-web" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/08/07/33-doxygen-et-l-integration-continue" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/07/19/32-je-me-presente-je-m-appelle-remi" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/07/19/31-nouvelles-recrues" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/06/02/30-databinding-swt-text-eclipse-33" />
  <rdf:li rdf:resource="http://www.cheminsdetraverse.info/index.php?2007/04/20/29-eclipse-plugin-tableau-de-proprietes" />
  </rdf:Seq>
  </items>
</channel>

<item rdf:about="http://www.cheminsdetraverse.info/index.php?2008/01/05/38-creer-une-boutique-en-ligne-partie-4-vue-compacte-et-multilinguisme">
  <title>Créer une boutique en ligne - Partie 4 : Vue compact, recherche et multi-linguisme.</title>
  <link>http://www.cheminsdetraverse.info/index.php?2008/01/05/38-creer-une-boutique-en-ligne-partie-4-vue-compacte-et-multilinguisme</link>
  <dc:date>2008-01-05T13:30:40+01:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>McGivrer</dc:creator>
  <dc:subject>Ruby on Rails</dc:subject>
  <description>Bon, nous avons déjà un bon début, la gestion des produits, la vue "boutique" détaillée. Nous allons étailler notre site avec une vue , plus compacte, et même ajouter une fonction "recherche". Enfin, nous passerons donc aux choses sérieures avec la mise en place du plugin Globalize pour ajouter le multi-linguisme à notre site. On en profitera pour passer un coup de polish sur l'ensemble des feuilles de styles, des templates rhtml et du code. Aller, HOP au boulot !</description>
  <content:encoded><![CDATA[<p>Bon, nous avons déjà un bon début, la gestion des produits, la vue "boutique" détaillée. Nous allons étailler notre site avec une vue , plus compacte, et même ajouter une fonction "recherche". Enfin, nous passerons donc aux choses sérieures avec la mise en place du plugin Globalize pour ajouter le multi-linguisme à notre site. On en profitera pour passer un coup de polish sur l'ensemble des feuilles de styles, des templates rhtml et du code. Aller, HOP au boulot&nbsp;!</p> <h4>Boutique&nbsp;: la vue compacte</h4>


<p>Nous allons modifier notre controlleur "Store" afin d'ajouter dans les vues <code>details</code> et <code>compact</code>, des tris sur plusieurs champs du modèle produit. En clair, nous pourrons trier celles-ci sur les titres, prix et date de disponibilité.</p>


<h3>le controller</h3>

<p>Commençons donc par le "StoreController"&nbsp;:</p>

<pre>
  def list
  #ordre de tri des produits
  so = sortorder
  sens = session[:sens]

  #Type d'affichage
  dm = displaymode
  case dm
    when &quot;compact&quot;
      @product_pages, @products = paginate :products, :per_page =&gt; 9, :order =&gt; so + &quot; &quot; + sens
      session[:dm] = &quot;compact&quot;
      render :action =&gt; &quot;list_compact&quot;
    when &quot;details&quot;
      @product_pages, @products = paginate :products, :per_page =&gt; 6, :order_by =&gt; so + &quot; &quot; + sens
      session[:dm] = &quot;details&quot;
      render :action =&gt; &quot;list&quot;
    else
      @product_pages, @products = paginate :products, :per_page =&gt; 9, :order =&gt; so + &quot; &quot; + sens
      session[:dm] = &quot;compact&quot;
      render :action =&gt; &quot;list_compact&quot;
  end
  end
</pre>


<p>Au début, vous pouvez voir 2 appels à des méthodes qui seront privée au controller: <code>sortorder</code> et <code>displaytype</code>. Ceci pour factoriser une peu notre code et le rendre plus lisible. En voici le code à glisser en fin du fichier du controller&nbsp;:</p>

<pre>
private 
  # permet de choisir le champ de tri pour la liste de produit.
  def sortorder
    if session[:s]==params[:s] 
      session[:sens] = (session[:sens] == &quot;asc&quot; ? &quot;desc&quot; : &quot;asc&quot; )
    else 
      session[:sens] = &quot;asc&quot;
    end
    s = params[:s] || session[:s] || &quot;1&quot;
    case s
      when &quot;1&quot;
        so = 'title'
      when &quot;2&quot;
        so = 'price'
      when &quot;3&quot;
        so = 'date_available'
    end
    session[:s] = s
    session[:prevs] = s
    return so
  end	

  # Positionne le type d'affichage souhaité pour la liste de produit.
  def displaytype
      dt = params[:dm] || session[:dm] || &quot;compact&quot;
  end

</pre>


<p>Nous sommes donc prêt à collecter les informations des produits, reste à les afficher...</p>


<h3>le layout et les vues</h3>


<p>La méthode (ou action) <code>list</code> est donc maintenant capable de trier et de choisir le type d'affichage. Nous devons maintenant modifier quelque-peu les vues pour le controller "Store"; en effet, une nouvelle vue fait son apparition&nbsp;: <code>list_compact.rhtml</code>. Elle est destinée à l'affichage de la vue compact des produits. Nous devrons également modifier le layout <code>store.rhtml</code>, pour ajouter les liens qui permettront le choix de vue et ceux des tris.</p>


<p>Layout <code>gamestore/app/views/layouts/store.rhtml</code>&nbsp;:</p>

<pre>
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
       &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;fr&quot; lang=&quot;fr&quot;&gt;
&lt;head&gt;
  &lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html;charset=UTF-8&quot; /&gt;
  &lt;title&gt;&lt;%= h &quot;GameStore&quot; %&gt;&lt;/title&gt;
  &lt;%= stylesheet_link_tag 'scaffold','store','main' %&gt;
  &lt;%= javascript_include_tag :defaults %&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id=&quot;header&quot;&gt;
    &lt;h1&gt;&lt;%= h &quot;GameStore&quot; %&gt;&lt;/h1&gt;
  &lt;/div&gt;
  &lt;div id=&quot;main&quot;&gt;
    &lt;p style=&quot;color: green&quot;&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;	
    &lt;%= render :partial =&gt; 'menu' %&gt;
    &lt;%= yield  %&gt;
  &lt;/div&gt;
  &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
  &lt;div id=&quot;footer&quot;&gt;
    &lt;p&gt;&lt;%= &quot;Copyright &amp;copy; 2008 - Boutique en ligne - démo - &quot; %&gt; &lt;%= link_to &quot;Contact&quot;,&quot;mailto:mcgivrer@gmail.com&quot; %&gt;&lt;/p&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>


<p>Pour simplifier l'écriture de l'ensemble de nos liens et rendre lisible le template, nous effectuerons la mise en valeur de ceux-ci, on précise visuellement quel champ sert de critère de tri),  à l'aide de helpers que nous écrirons dans le fichier  <code>gamestore/app/helpers/store_helpers.rb </code>&nbsp;:</p>


<p>le première servira à afficher et décorer les liens des modes d'affichage&nbsp;:</p>
<pre>
  def setDisplayMode(libelle,mode,title=&quot;change le mode d'affichage&quot;)
    if session[:dm]==mode
        link_to libelle,   { :action =&gt; 'list', :dm =&gt; mode}, :class=&gt;&quot;selected&quot;, :title=&gt; title
    else
        link_to libelle,   { :action =&gt; 'list', :dm =&gt; mode}, :title=&gt; title
    end
  end
</pre>


<p>le deuxième servira à laffichage des liens sur les critères de tri&nbsp;:</p>
<pre>
  # fixe le champ qui servira de tri
  def setSortField(libelle,value,title=&quot;Trier sur cette valeur&quot;)
    if session[:s] == value
      link_to libelle,   { :action =&gt; 'list', :s =&gt; value}, :class =&gt; &quot;selected&quot;, :title =&gt; title
    else
      link_to libelle,   { :action =&gt; 'list', :s =&gt; value}, :title =&gt; title
    end
  end
</pre>


<p>Dans la partie <code>#main</code> du layout, nous ajoutons au-dessus de <code>&lt;%= yield %&gt;</code> une balise <code>&lt;DIV/&gt;</code> contenant une liste à puce qui délimitera chaque lien. La mise en forme de cette liste sera assurée par une CSS (voir <code>gamestore/public/stylesheets/store.css</code>.</p>

<pre>
&lt;div id=&quot;main&quot;&gt;
  &lt;p style=&quot;color: green&quot;&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;	
  &lt;div id=&quot;menu&quot;&gt;
    &lt;ul class=&quot;display&quot;&gt;
      &lt;li&gt;&lt;%= h &quot;Affichage&quot; %&gt; &lt;/li&gt;
      &lt;li&gt;&lt;%= setDisplayMode &quot;Détaillé&quot;, 'details' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setDisplayMode &quot;Compact&quot;, 'compact' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= &quot;Trier par :&quot; %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setSortField &quot;Titre&quot;, '1' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setSortField &quot;Prix&quot;, '2' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setSortField &quot;Disponibilité&quot;, '3' %&gt;&lt;/li&gt;
     &lt;/ul&gt;
     &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
   &lt;/div&gt;
  &lt;%= yield  %&gt;
&lt;/div&gt;
</pre>


<h4>Utiliser le rendu partiel</h4>


<p>Ok, nous avons une belle ligne de liens. Pour éviter de surcharger <code>store.rhtml</code> nous allons "partialiser" le menu en copiant le contenu délimité par la balise <code>&lt;div/&gt;</code> identifiée par <code>"menu"</code> dans un fichier <code>gamestore/app/views/store/_menu.rhtml</code>.</p>

<pre>
  &lt;div id=&quot;menu&quot;&gt;
    &lt;ul class=&quot;display&quot;&gt;
      &lt;li&gt;&lt;%= h &quot;Affichage&quot; %&gt; &lt;/li&gt;
      &lt;li&gt;&lt;%= setDisplayMode &quot;Détaillé&quot;, 'details' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setDisplayMode &quot;Compact&quot;, 'compact' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= &quot;Trier par :&quot; %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setSortField &quot;Titre&quot;, '1' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setSortField &quot;Prix&quot;, '2' %&gt;&lt;/li&gt;
      &lt;li&gt;&lt;%= setSortField &quot;Disponibilité&quot;, '3' %&gt;&lt;/li&gt;
     &lt;/ul&gt;
     &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
   &lt;/div&gt;
</pre>


<p>Et en lieu et place de ces lignes, dans <code>store.rhtml</code>, nous allons écrire&nbsp;:</p>


<pre>&lt;%= render :partial =&gt; "menu" %&gt;</pre>


<p>Voila du code un peu plus professionnel. Continuons nos opérations de partialisation en pratiquant la même démarche avec les templates de <code>list.rhtml</code> et <code>list_compact.rhtml</code>: créons les fichiers <code>_item.rhtml</code> et <code>_copact.rhtml</code> qui serviront à l'affichage d'un produit dans les deux mode de liste.</p>


<h5>List détaillée</h5>


<p><code>list.rhtml</code></p>

<pre>
&lt;div id=&quot;content&quot;&gt;
  &lt;div class=&quot;product_list&quot;&gt;
  &lt;% if @products %&gt;
    &lt;% for @product in @products %&gt;
    &lt;%= render :partial =&gt; 'item' %&gt;
    &lt;% end %&gt;
  &lt;% end %&gt;
  &lt;/div&gt;
  &lt;div class=&quot;pager&quot;&gt;
    &lt;%= link_to 'Page précédente', { :page =&gt; @product_pages.current.previous }, :class =&gt;&quot;button prev&quot; if @product_pages.current.previous %&gt;
    &lt;%= link_to 'Page suivante', { :page =&gt; @product_pages.current.next }, :class =&gt;&quot;button prev&quot; if @product_pages.current.next %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</pre>


<p><code>_item.rhtml</code></p>
<pre>
&lt;div class=&quot;product item&quot;&gt;
&lt;% if @product.image %&gt;
  &lt;%= image_tag url_for_file_column(&quot;product&quot;,&quot;image&quot;,&quot;mini&quot;), :class =&gt; &quot;product&quot; %&gt;
&lt;% end %&gt;
  &lt;div class=&quot;infos&quot;&gt;
    &lt;h2&gt;&lt;%= link_to @product.title, {:action =&gt; 'show', :id =&gt; @product.id}%&gt;&lt;/h2&gt;
    &lt;div class=&quot;price&quot;&gt;&lt;%= h fmt_currency(@product.price) %&gt;&lt;/div&gt;
    &lt;div class=&quot;description&quot;&gt;&lt;%= h @product.description%&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
</pre>


<h5>Liste compacte&nbsp;:</h5>

<ul>
<li><code>list_compact.rhtml</code></li>
</ul>
<pre>
&lt;div id=&quot;content&quot;&gt;
  &lt;div class=&quot;list&quot;&gt;
  &lt;% for @product in @products %&gt;
    &lt;%= render :partial =&gt; 'compact' %&gt;
  &lt;% end %&gt;
  &lt;/div&gt;
  &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
  &lt;div class=&quot;pager&quot;&gt;
    &lt;%= link_to &quot;Page suivante&quot;.t, { :page =&gt; @product_pages.current.next }, :class =&gt;&quot;button prev&quot; if @product_pages.current.next %&gt;
    &lt;%= link_to &quot;Page précédente&quot;.t, { :page =&gt; @product_pages.current.previous }, :class =&gt;&quot;button next&quot; if @product_pages.current.previous %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</pre>

<ul>
<li><code>_compact.rhtml</code></li>
</ul>
<pre>
&lt;div class=&quot;compact&quot;&gt;
&lt;% if @product.image %&gt;
  &lt;%= image_tag url_for_file_column(&quot;product&quot;,&quot;image&quot;,&quot;mini&quot;), :class =&gt; &quot;cover&quot; %&gt;
&lt;% end %&gt;
  &lt;div class=&quot;infos&quot;&gt;
    &lt;h2&gt;&lt;%= link_to @product.title, {:action =&gt; 'show', :id =&gt; @product.id}, :title =&gt; (h @product.description) %&gt;&lt;/h2&gt;
    &lt;div class=&quot;price&quot;&gt;&lt;%= fmt_currency(@product.price) %&gt;&lt;/div&gt;
    &lt;div class=&quot;availability&quot;&gt;&lt;%= &quot;Disponibilité&quot; + &quot;&lt;br /&gt;&quot; + @product.date_available.loc(&quot;%d/%m/%Y&quot;) %&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
</pre>


<p>Nous avons donc maintenant des affichages clair et des templates bien séparés, permettant une maintenance aisée. Pour les feuilles de styles, je vous laisse libre de parcourir les fichiers <code>main.css</code>, <code>store.css</code>, <code>products.css</code>.</p>


<p><strong><ins>Note :</ins></strong></p>


<p><ins>Vous aurez sans doute remarqué que nous affichons même les simples chaînes de caractères des labels via une commande rhtml <code>&lt;%= "..."%&gt;</code>. Vous en comprendrez l'intérêt lorsque nous utiliserons le plugins de gestion du multi-linguisme <code>Globalize</code>.</ins></p>


<p>Et donc, avec toutes ces petites modifications et les fichiers CSS ajustés vous obtiendrez cette belle page&nbsp;:</p>


<p><a href="/images/ruby_on_rails/gamestore/011_gamestore_compact_list.jpg" hreflang="fr" title="La liste des produits dans sa version compacte et triable !" rel="lightbox"><img src="/images/ruby_on_rails/gamestore/320x-011_gamestore_compact_list.png" alt="Liste compacte" style="display:block; margin:0 auto;" /></a></p>

<h4>fonction de Recherche</h4>


<p>Un site d'achat en ligne doit permettre de trouver rapidement un produit, sans devoir parcourir toutes les page de la boutique. Une fonction de recherche s'impose donc d'elle-même.
Ne réinventons pas la roue, et utilisons ce qui existe, c'est le "leit motiv" de Rails. Aussi, penchons nous sur la page "<a href="http://wiki.rubyonrails.com/rails/pages/TextSearch" hreflang="en">TextSearch</a>" du wiki officiel de Rails, et utilisons la librairie proposée <code>search.lib</code>.</p>


<p>Ensuite, editer <code>app/models/product.rb</code> et ajouter&nbsp;:</p>

<pre>
__require_dependency &quot;search&quot;__

class Product &lt; ActiveRecord::Base

  file_column :image,
    :magick =&gt; { :geometry=&gt;&quot;400x600&quot;, 
      :versions=&gt;{ :medium =&gt; &quot;320x200&quot;, :thumb=&gt; &quot;138x188&quot;, :mini=&gt; &quot;60x90&quot; } }

  __searches_on :title, :description__

  validates_presence_of :title, :description

  validates_file_format_of :image, :in =&gt; [&quot;gif&quot;, &quot;jpg&quot;, &quot;png&quot;]
  validates_filesize_of :image, :in =&gt; 1.kilobytes..250.kilobytes

end
</pre>


<p>Nous avons dons précisé sur quel champs portait notre moteur de recherche. Attention, la recherche pratiqué et très simple. on recherchera la phrase passée dans ces champs.</p>


<p>Il nous faut donc traiter cette recherche.
Commençons par ajouter un champs de recherche dans notre vue store. Comme nous connaissons les rendu partiels, profitons en et créons un <code>_menu.rhtml</code> dans <code>app/layouts/store</code>:</p>

<pre>
&lt;% form_tag ({:controller =&gt; 'store' , :action =&gt; 'search'}, :name=&gt;'search', :class =&gt; &quot;search&quot;) do %&gt;
   &lt;input type=&quot;text&quot; name = &quot;searchtext&quot; value=&quot;&lt;%= h (params[:searchtext]||&quot;Rechercher ...&quot;) %&gt;&quot; 
    cols=&quot;20&quot;  
    onclick=&quot;javascript:this.value='';&quot; 
    onblur=&quot;javascript:if( this.value != '' ) document.search.submit(); else this.value='&lt;%= h (params[:searchtext]||&quot;Rechercher ...&quot;) %&gt;'&quot; /&gt;
   &lt;%= image_submit_tag &quot;/images/icons/search.png&quot;, :value=&gt;&quot;&quot; %&gt;
&lt;% end %&gt;
</pre>


<p>Reprenons notre <code>store_controller.rb</code> et ajoutons une méthode <code>search</code>.</p>

<pre>
  def search
    if params[:searchtext]!=&quot;&quot;
      @products = Product.search params[:searchtext]
    else
      flash[:notice] = &quot;Vous devez saisir les termes de votre recherche dans la zone texte prévue à cet effet&quot;
    end
    if !@products
      flash[:notice] = &quot;Il n'y a pas de résultats pour la recherche '&quot; + params[:searchtext] + &quot;'&quot;
    end
    render :action =&gt; &quot;list&quot;
  end
</pre>


<p>Ajoutons quelques test dans <code>list.rhtml</code> en remplaçant la <code>div</code> "pager" par ces lignes&nbsp;:</p>
<pre>]
  &lt;% if !params['searchtext'] %&gt;
    &lt;div class=&quot;pager&quot;&gt;
      &lt;%= link_to 'Page précédente'.t, { :page =&gt; @product_pages.current.previous }, :class =&gt;&quot;button prev&quot; if @product_pages.current.previous %&gt;
      &lt;%= link_to 'Page suivante'.t, { :page =&gt; @product_pages.current.next }, :class =&gt;&quot;button prev&quot; if @product_pages.current.next %&gt;
    &lt;/div&gt;
  &lt;% else %&gt;
  &lt;%= link_to &quot;Retour à la boutique&quot;.t, {:controller=&gt;&quot;store&quot;, :action=&gt;&quot;list&quot;}, :class =&gt;&quot;button back&quot; %&gt;
  &lt;% end %&gt;
</pre>


<p>Et maintenant, appeler votre page <a href="http://localhost:3000/">http://localhost:3000/</a>. et tester votre recherche. Vos constaterez qu'une certaine dynamique a été ajouter par le biais d'un peu de javascript sur le champs <code>search</code> du formulaire.</p>


<p>That's all&nbsp;!</p>


<h3>Le Multi-linguisme</h3>


<h4>Installation du plugin Globalize</h4>


<p>Nous aurons besoin de ce super plugin qu'est Globalize. Installons le dans notre application&nbsp;:</p>


<p><code>script/plugin install http://svn.globalize-rails.org/svn/globalize/branches/for-1.2</code></p>


<p>Si vous regardez dans le répertoire <code>gamestore/vendor/plugin/</code> vous constaterez l'apparition d'un répertoire <code>for-1.2</code>. Renommez celui-ci en <code>globalize</code></p>


<h4>Configuration et utilisation de Globalize</h4>


<p>Ensuite, lancer la commande <code>rake globalize:setup</code>. Celle-ci lancera la création et le remplissage de nouvelles tables dans votre base de données ( <code>globalize_countries</code>, <code>globalize_languages</code>, <code>globalize_translations</code>). Ces tables sont constitues le coeur du système mis en place par ce plugin.</p>


<p>Dans votre fichier <code>environnement.rb</code>, tout à la fin ajouter les lignes ci-dessous&nbsp;:</p>

<pre>
include Globalize
Globalize::Locale.set_base_language 'fr-FR'
Globalize::LOCALES = {'de' =&gt; 'de-DE',
           'en' =&gt; 'en-US',
           'es' =&gt; 'es-ES',
           'fr' =&gt; 'fr-FR'}.freeze
</pre>


<h4>Choix automatique de la locale</h4>


<p>Ensuite, dans <code>ApplicationController</code> (app/controllers/application.rb) nous allons ajouter la détection de la langue supportée par le navigateur du visiteur. Pour cela, ajoutons la méthode ci-dessous&nbsp;:</p>

<pre>
...
  #ne pas oublier d'automatiser l'appel
  before_filter :set_locale

  # Récupération automatique de la &quot;locale&quot; du navigateur.
  def set_locale
    default_locale = 'fr-FR'
    request_language = request.env['HTTP_ACCEPT_LANGUAGE']
    request_language = request_language.nil? ? nil : 
    request_language[/[^,;]+/]

    @locale = params[:locale] || session[:locale] || request_language || default_locale
    session[:locale] = @locale
    begin
      Locale.set @locale
    rescue
      Locale.set default_locale
    end
  end
...
</pre>


<h4>choix manuel de la langue</h4>


<p>Maintenant, pour rendre traductible l'ensemble de vos libellés dans vos fichier rhtml, vous devrez passer par une commande <code>erb</code> du type <code>&lt;%= "Mon libellé".t %&gt;</code>, ainsi, la chaine <code>"Mon libellé"</code> devient traductible par l'adjonction de l'appel à la méthode "<code>.t</code>", et une entrée dans la table <code>globalize_translations</code> sera créer dès le premier affichage de la page le contenant dans la langue par défaut (celle choisie dans application.rb).</p>


<p>Il ne nous manque plus qu'une petite interface pour choisir la langue souhaitée sur notre boutique.</p>


<p>Nous utiliserons une fois encore le rendu partiel <code>_menu.rhtml</code> depuis <code>store.rhtml</code>:</p>


<p>Ajoutez les ligne ci-dessous dans l'id "menu"&nbsp;:</p>
<pre>
   &lt;ul class=&quot;translate&quot;&gt;
    &lt;li&gt;&lt;%= setLinkLanguage('fr-FR',&quot;Français&quot;.t) %&gt;&lt;/li&gt;
    &lt;li&gt;&lt;%= setLinkLanguage('en-EN',&quot;Anglais&quot;.t) %&gt;&lt;/li&gt;
    &lt;li&gt;&lt;%= setLinkLanguage('de-DE',&quot;Allemand&quot;.t) %&gt;&lt;/li&gt;
    &lt;li&gt;&lt;%= setLinkLanguage('es-ES',&quot;Espagnol&quot;.t) %&gt;&lt;/li&gt;
   &lt;/ul&gt;
</pre>


<p>et la méthode setLinkLanguage à placer dans <code>application_helpers.rb</code>&nbsp;:</p>
<pre>
#Défini la langue du site pour la session.
def setLinkLanguage(lang,description)
  if session[:locale]==lang
    link_to image_tag(&quot;flags/&quot;+lang+&quot;.png&quot;), {:controller =&gt; controller.controller_name, :action =&gt; controller.action_name, :locale =&gt; lang, :id =&gt; params[:id]}, :title=&gt;description, :class=&gt;&quot;selected&quot;
  else
    link_to image_tag(&quot;flags/&quot;+lang+&quot;.png&quot;), {:controller =&gt; controller.controller_name, :action =&gt; controller.action_name, :locale =&gt; lang, :id =&gt; params[:id]}, :title=&gt;description
  end
end
</pre>

<p>Constatez l'apparition des  <code>.t</code> dans tous les libellés dans les <a href="codes sources livrés">codes sources livrés</a> en exemple.</p>


<h4>Une interface de gestion des traductions</h4>


<p>L'article situé à <a href="cet endroit">cet endroit</a> est un excellent tutorial pour la mise en place ultra rapide d'une interface de traduction. je vous laisse seul juge.</p>

<ul>
<li>Controller <code>app/controllers/admin/translate_controller.rb</code></li>
</ul>
<pre>
class Admin::TranslateController &lt; ApplicationController

  layout &quot;admin&quot;

  def index
    @view_translations = ViewTranslation.find(
      :all, 
      :conditions =&gt; [ 'text IS NULL AND language_id = ?', Locale.language.id ], 
      :order =&gt; 'tr_key')
  end

  def translation_text
    @translation = ViewTranslation.find(params[:id])
    render :text =&gt; @translation.text || &quot;&quot;  
  end

  def set_translation_text
    @translation = ViewTranslation.find(params[:id])
    previous = @translation.text
    @translation.text = params[:value]
    @translation.text = previous unless @translation.save
    render :partial =&gt; &quot;translation_text&quot;, :object =&gt; @translation.text  
  end
end
</pre>


<p>Ensuite, passons aux templates de rendu&nbsp;:</p>

<ul>
<li><code>app/views/admin/translate</code></li>
</ul>
<pre>
&lt;div class=&quot;translations&quot;&gt;
&lt;% base_language_only do -%&gt;
  &lt;div id=&quot;language&quot;&gt;&lt;h1&gt;Please choose language for translation&lt;/h1&gt;&lt;/div&gt;
&lt;% end -%&gt;

&lt;% not_base_language do -%&gt;
  &lt;div id=&quot;language&quot;&gt;&lt;h1&gt;&lt;%= &quot;Language: &quot; + Locale.language.native_name %&gt;&lt;/h1&gt;&lt;/div&gt;
  &lt;div&gt;
    &lt;% @view_translations.each do |tr| -%&gt;
      &lt;%= render :partial =&gt; 'translation_form', :locals =&gt; {:tr =&gt; tr}%&gt;
    &lt;% end -%&gt;
  &lt;/div&gt;
&lt;% end  -%&gt;
&lt;/div&gt;
</pre>

<ul>
<li><code>app/views/admin/translate/_translation_text.rhtml</code>&nbsp;: L'affichage de la traduction d'un libellé dans la langue sélectionnée:</li>
</ul>
<pre>
&lt;%= translation_text || '[no translation]' %&gt;
</pre>

<ul>
<li><code>app/views/admin/translate/_translation_form.rhtml</code>&nbsp;: Le formulaire de saisie via un "edit in place" de la traduction d'un libellé:</li>
</ul>
<pre>
&lt;!--[form:translate]--&gt;
&lt;p class=&quot;item&quot;&gt;
&lt;label for=&quot;tr_&lt;%= tr.id %&gt;&quot;&gt;&lt;%=tr.tr_key%&gt;&lt;/label&gt;&lt;br /&gt;
&lt;span id=&quot;tr_&lt;%= tr.id %&gt;&quot; class=&quot;translation&quot;&gt;
&lt;%= render :partial =&gt; 'translation_text', :object =&gt; tr.text %&gt;
&lt;/span&gt;
&lt;%= in_place_editor &quot;tr_#{tr.id}&quot;, 
    :url =&gt; { :action =&gt; :set_translation_text, :id =&gt; tr.id },
    :load_text_url =&gt; url_for({ :action =&gt; :translation_text, :id =&gt; tr.id })%&gt;
&lt;/p&gt;
&lt;!--[eoform:translate]--&gt;
</pre>

<ul>
<li><code>app/layouts/admin.rhtml</code> enfin, le layout d'affichage des outils d'administration (/admin)&nbsp;:</li>
</ul>
<pre>
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
       &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;

&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;en&quot; lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html;charset=UTF-8&quot; /&gt;
    &lt;title&gt;&lt;%= &quot;Administration du site&quot;.t %&gt;&lt;/title&gt;
    &lt;%= stylesheet_link_tag 'scaffold','main','admin','product' %&gt;
    &lt;%= javascript_include_tag :defaults %&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;header&quot;&gt;
      &lt;h1&gt;GameStore&lt;/h1&gt;
      &lt;h2&gt;&lt;%= &quot;Administration&quot;.t %&gt;&lt;/h2&gt;
    &lt;/div&gt;
    &lt;div id=&quot;main&quot;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;ul&gt;
          &lt;li&gt;&lt;%= link_to &quot;Boutique&quot;.t, { :controller =&gt; '/store', :action=&gt;'index' } %&gt;&lt;/li&gt;
          &lt;li&gt;&lt;%= link_to &quot;Produit&quot;.t, { :controller =&gt; 'admin/product', :action=&gt;'index' } %&gt;&lt;/li&gt;
          &lt;li&gt;&lt;%= link_to &quot;Traduction&quot;.t, { :controller =&gt; 'admin/translate', :action=&gt;'index' } %&gt;&lt;/li&gt;
        &lt;/	ul&gt;
      &lt;/div&gt;
      &lt;div id=&quot;content&quot;&gt;
        &lt;p class=&quot;notice&quot;&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;
        &lt;%= yield  %&gt;
      &lt;/div&gt;
      &lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div id=&quot;footer&quot;&gt;
      &lt;p&gt;&lt;%= &quot;Copyright &amp;copy; 2008 - Boutique en ligne - démo - &quot;.t %&gt;
&lt;%= link_to &quot;Contact&quot;.t,&quot;mailto:mcgivrer@gmail.com&quot; %&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>


<p>Enfin, on ajoute quelques définitions dans notre feuille de styles <code>public/stylesheets/admin.css</code>:</p>

<pre>[css]
/*---- traduction du site ----*/
.translations *{
	padding:2px:
	margin:2px;
}
.translations .item {
	border-bottom:1px dotted #ddd;
	margin:4px;
	padding:2px;
}
.translations .item label{
	font-weight: bold;
}
.translations .item label .number{
	font-size: 7pt;
}
.translations .item .translation {
	color:navy;
	padding:2px;
	margin:4px;
	border:1px solid #ddd;
}
</pre>


<p>Et voilà&nbsp;! appeler l'url <a href="http://localhost:3000/admin/translate/?locale=es-ES">http://localhost:3000/admin/translate/?locale=es-ES</a> et vous serez en position de saisir la traduction en espagnol (es-ES) de tous les libellés non-encore traduits.</p>


<p><a href="/images/ruby_on_rails/gamestore/Capture1-translate-list-keys.gif" title="fr" rel="lightbox[L'interface de traduction du site]"><img src="/images/ruby_on_rails/gamestore/Capture1-translate-list-keys.TN__.png" alt="Liste des libellé à traduire" style="display:block; margin:0 auto;" /></a></p>


<p>pour editer un des libellés, passer votre souris sur celui-ci, il apparait alors en surligné jaune:</p>


<p><img src="/images/ruby_on_rails/gamestore/Capture1-translate-edit-key.gif" alt="Edition d&#039;un libellé" style="display:block; margin:0 auto;" /></p>


<p>Un clic et vous voilà en édition sur celui.</p>


<p><img src="/images/ruby_on_rails/gamestore/Capture2-translate-edit-key.gif" alt="Modification d&#039;une valeur" style="display:block; margin:0 auto;" /></p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/12/31/37-creer-une-boutique-en-ligne-avec-rails-partie-3-la-boutique">
  <title>Créer une boutique en ligne avec Rails - Partie 3 : la boutique</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/12/31/37-creer-une-boutique-en-ligne-avec-rails-partie-3-la-boutique</link>
  <dc:date>2007-12-31T16:34:02+01:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>McGivrer</dc:creator>
  <dc:subject>Ruby on Rails</dc:subject>
  <description>Après avoir choisi nos outils et avoir mis en place notre première partie du site avec la gestion des produits, nous allons bâtir la partie visible par le commun des mortels du site: la boutique (StoreController).


Dans la prochaine partie, nous verrons comment faire une vue plus compact, nous ajouterons une possibilité de recherche, et nous ajouterons le multi-linguisme dans note site.
Et dans une future partie,  nous enchaînerons avec la sécurisation de la partie gestion/administration du site à l'aide des plugins Rails acts_as_authentified et acl_system.</description>
  <content:encoded><![CDATA[<p>Après avoir <a href="http://www.cheminsdetraverse.info/index.php?2007/12/18/35-creer-une-application-de-vente-en-ligne-partie-1-choix-des-outils" hreflang="fr" title="Partie 1&nbsp;: le choix des outils">choisi nos outils</a> et avoir mis en place notre première partie du site avec <a href="http://www.cheminsdetraverse.info/index.php?2007/12/31/36-creer-une-boutique-en-ligne-avec-rails-partie-2-la-gestion-des-produits" hreflang="fr" title="Partie 2&nbsp;: La gestion des produits">la gestion des produits</a>, nous allons bâtir la partie visible par le commun des mortels du site: la boutique (<code>StoreController</code>).</p>


<p>Dans la prochaine partie, nous verrons comment faire une vue plus compact, nous ajouterons une possibilité de recherche, et nous ajouterons le multi-linguisme dans note site.
Et dans une future partie,  nous enchaînerons avec la sécurisation de la partie gestion/administration du site à l'aide des plugins Rails <code>acts_as_authentified</code> et <code>acl_system</code>.</p> <h3>La boutique&nbsp;:  StoreController</h3>


<p>Bon, ben voila, nous avons géré nos produits. Mais le but est de faire un super site de vente en ligne, aussi, nous ne pouvons pas laisser l'accessibilité à cette gestion, nous devons faire un autre controlleur qui sera charger d'afficher la boutique:  StoreController.</p>


<p>Un coup de générateur&nbsp;: <code>script/generate controller store index list show</code> oubien passez par les menus de rails.vim&nbsp;: <code>Plugin-&gt;Rails-&gt;Generate-&gt;Controller</code> azvec comme paramètres <code>store index list show</code>.</p>


<p>Nous avons un nouveau controller tout neuf, mais qui ne comporte que 3 méthodes vides: <code>index</code>, <code>list</code> et <code>show</code>.</p>

<pre>[ruby]
class StoreController&lt;ApplicationController
  def list
  end

  def index
  end

  def show
  end

end
</pre>


<p>Bon,c'est pas tout ca, mais on a une boutique à faire.</p>


<p>Donc, commençons par récupérer les informations dont nous avons besoins, regardons le contenu de la méthode <code>list</code>, nous devons la copier pour lire les infos des produits:</p>

<pre>[ruby]
  def list
    @product_pages, @products = paginate :products, :per_page =&gt; 10, :order =&gt; 'title'
  end
</pre>


<p>Hop Copier/Coller dans notre nouveau <code>StoreController</code> en lieu et place la méthode générée. Nous avons donc nos produits à disposition en mémoire.
Notez au passage le paramètre <code>:order</code> que nous ajoutons pour trier nos produit sur le <code>title</code> de façon ascendante.</p>


<p>OK, maintenant, la vue&nbsp;: <code>gamestore/app/view/layouts/store.rhtml</code>&nbsp;: c'est le template du niveau <code>controller</code>, c'est a dire que toutes les actions effectuées dans ce controleur hériterons de la mise en forme de ce fichier. c'est le canevas pour nos méthodes <code>index</code>, <code>list</code> et <code>show</code>.</p>

<pre>[html]
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
       &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xml:lang=&quot;fr&quot; lang=&quot;fr&quot;&gt;
&lt;head&gt;
  &lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html;charset=UTF-8&quot; /&gt;
  &lt;title&gt;GameStore&lt;/title&gt;
  &lt;%= stylesheet_link_tag 'scaffold','main','store' %&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;div id=&quot;header&quot;&gt;
		&lt;h1&gt;GameStore&lt;/h1&gt;
	&lt;/div&gt;
	&lt;div id=&quot;main&quot;&gt;
		&lt;p style=&quot;color: green&quot;&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;				
			&lt;%= yield  %&gt;
	&lt;/div&gt;
	&lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
	&lt;div id=&quot;footer&quot;&gt;
		&lt;p&gt;Copyright &amp;copy; 2008 - Boutique en ligne - démo - &lt;a href=&quot;mailto:mcgivrer@gmail.com&quot;&gt;Contact&lt;/a&gt;&lt;/p&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>

<p>Comme vous pouvez le constater, nous avons ajouté 2 feuilles de styles&nbsp;: <code>main</code> et <code>store</code>. la première défini la look globale et le zoning de notre boutique, tandis que la deuxième permettra de définir toute la mise en forme graphique des éléments de cette page, comme les titres, le description, les images, etc...
vous pouvez les télécharger ci-dessous&nbsp;:</p>
<ul>
<li><code><a href="http://delorme.frederic.free.fr/dotclear/files/rails/gamestore/main.css" hreflang="fr">gamestore/public/stylesheets/main.css</a></code></li>
<li><code><a href="http://delorme.frederic.free.fr/dotclear/files/rails/gamestore/store.css" hreflang="fr">gamestore/public/stylesheets/store.css</a></code></li>
</ul>
<p>Pour plus d'explication, nous pouvons résumer main.css par la division de la page de la boutique en 3 zones principales&nbsp;: #Header, #Main et #Footer.</p>


<p>Mais il nous manque le template de rendu pour la méthode <code>list</code>.  le voilà dans <code>gamestore/app/views/store/list.rhtml</code>:</p>

<pre>[html]
&lt;div id=&quot;content&quot;&gt;
	&lt;div class=&quot;product_list&quot;&gt;
	&lt;% for @product in @products %&gt;
		&lt;div class=&quot;product item&quot;&gt;
		&lt;% if @product.image %&gt;
			&lt;%= image_tag url_for_file_column(&quot;product&quot;,&quot;image&quot;,&quot;mini&quot;), :class =&gt; &quot;product&quot; %&gt;
		&lt;% end %&gt;
			&lt;div class=&quot;infos&quot;&gt;
				&lt;h2&gt;&lt;%= link_to @product.title, {:action =&gt; 'show', :id =&gt; @product.id}%&gt;&lt;/h2&gt;
				&lt;div class=&quot;price&quot;&gt;&lt;%= h fmt_currency(@product.price) %&gt;&lt;/div&gt;
				&lt;div class=&quot;description&quot;&gt;&lt;%= h @product.description%&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
		&lt;/div&gt;
	&lt;% end %&gt;
	&lt;/div&gt;
&lt;/div&gt;
</pre>


<p>Relançons notre serveur Rails (WeBrick) et appelons <a href="http://localhost:3000/store/list">http://localhost:3000/store/list</a>&nbsp;:</p>


<p><a href="/images/ruby_on_rails/gamestore/009_StoreController.jpg" hreflang="fr" title="La vue list du StoreController" rel="lightbox"><img src="/images/ruby_on_rails/gamestore/320x-009b_StoreController.jpg.png" alt="La vue list du StoreController" style="display:block; margin:0 auto;" /></a></p>


<p>Voila une belle liste de produit. Mais nous devons également définir les méthodes <code>index</code> et <code>show</code>:</p>
<pre>[ruby]
def index
  redirect_to :action=&gt;'list'
end

def show
@product = Product.find(params[:id])
end
</pre>

<p>Nous redirigeons la page index vers l'affichage de la liste, et la méthode show nécessite un nouveau template <code>gamestore/app/view/store/show.rhtml</code>&nbsp;:</p>
<pre>[ruby]
&lt;div id=&quot;content&quot;&gt;
&lt;div class=&quot;item&quot;&gt;
	&lt;% if @product.image %&gt;
		&lt;%= image_tag url_for_file_column(&quot;product&quot;,&quot;image&quot;,&quot;thumb&quot;) %&gt;
	&lt;% end %&gt;
	&lt;h2&gt;&lt;%= h @product.title %&gt;&lt;/h2&gt;
	&lt;div class=&quot;description&quot;&gt;&lt;%= h @product.description%&gt;&lt;/div&gt;
	&lt;hr size=&quot;2&quot; noshadow=&quot;noshadow&quot; /&gt;
	&lt;div class=&quot;price&quot;&gt;
		&lt;%= h @product.price %&gt;€
	&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;clear&quot;&gt;&lt;/div&gt;
&lt;%= link_to &quot;Continuer vers la boutique&quot;, {:controller =&gt; 'store', :action =&gt; 'list'} %&gt;
&lt;/div&gt;
</pre>

<p>Cliquez sur l'un des titres de produit, et vous devriez arriver sur la page du produit, par exemple <a href="http://localhost:3000/store/show/13">http://localhost:3000/store/show/13</a>&nbsp;:</p>


<p><a href="/images/ruby_on_rails/gamestore/010_StoreController_show.jpg" hreflang="fr" title="Affichage du détail d&#039;un produit (soit la page du produit dans la boutique)" rel="lightbox"><img src="/images/ruby_on_rails/gamestore/320x-010_StoreController_show.png" alt="Page d&#039;un produit" style="display:block; margin:0 auto;" /></a></p>


<p>Toutes les actions de cette partie sont codées et opérationnelles.</p>


<h3>Bon, et maintenant&nbsp;?</h3>


<p>Commençons par faire en sorte qu'il n'y ait plus besoin de spécifier un répertoire dans l'url pour pointer sur le bon controller. pour cela, allons faire un tour du côté du fichier <code>config/routes.rb</code>, et ajouter la ligne suivante&nbsp;:</p>

<pre>[ruby]
map.connect '', :controller =&gt; &quot;store&quot;
</pre>


<p>Ensuite, aller dans le répertoire public, et renomer (ou supprimer, comme vous préférez) le fichier <code>public/index.html</code> en <code>index.old.html</code>. Ce fichier est en fait l'accueil par défaut de toutes les application générée avec Rails.</p>


<p>Voila, redémarez le serveur rails (Webrick) et ouvrez à nouveau votre navigateur sur l'url <a href="http://localhost:3000/" hreflang="en">http://localhost:3000/</a>. Maintenant, vous tombez directement sur votre liste de produit disponible dans votre boutique ;)</p>


<p>Dans le "prochain épisode" nous créerons une deuxième vue Produit avec un affichage plus compact, et nous rendrons notre site multilingue à l'aide du plugin "<a href="http://globalize.rubyforge.org/" hreflang="en">Globalize</a>".</p>


<p>Vous pouvez télécharger l'application rails issue de ce tutorial à cette adresse&nbsp;:</p>
<ul>
<li><a href="http://delorme.frederic.free.fr/dotclear/files/rails/gamestore/rails-gamestore_step_2.tar.gz">http://delorme.frederic.free.fr/dotclear/files/rails/gamestore/rails-gamestore_step_2.tar.gz</a></li>
</ul>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/12/31/36-creer-une-boutique-en-ligne-avec-rails-partie-2-la-gestion-des-produits">
  <title>Créer une boutique en ligne avec Rails - Partie 2 : Gestion des produits</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/12/31/36-creer-une-boutique-en-ligne-avec-rails-partie-2-la-gestion-des-produits</link>
  <dc:date>2007-12-31T16:32:01+01:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>McGivrer</dc:creator>
  <dc:subject>Ruby on Rails</dc:subject>
  <description>Après une partie 1 qui nous a permis de choisir notre environnement de développement, nous allons enfin entrer dans le vif du sujet avec la mise en place de la gestion des produits de notre boutique en ligne, avec un minimum de travail, et ce, grâce à Rails et à ses nombreux générateurs (Model, Scaffold, Controller). En route !</description>
  <content:encoded><![CDATA[<p>Après une <a href="http://www.cheminsdetraverse.info/index.php?2007/12/18/35-creer-une-boutique-en-ligne-partie-1-choix-des-outils" hreflang="fr" title="Aller voir la partie 1&nbsp;: choix des outils">partie 1</a> qui nous a permis de choisir notre environnement de développement, nous allons enfin entrer dans le vif du sujet avec la mise en place de la gestion des produits de notre boutique en ligne, avec un minimum de travail, et ce, grâce à Rails et à ses nombreux générateurs (Model, Scaffold, Controller). En route&nbsp;!</p> <h3>Une première application&nbsp;?</h3>


<p>Oui, bon, est-ce bien nécessaire de refaire l'éternel "Hello World"&nbsp;? Et bien dans le cas de Rails, dans tous les cas, on y coupe pas, et en plus ça se fait tout seul. Il suffit de lancer un simple&nbsp;: <code>rails gamestore</code> dans votre console. Mais si vous êtes sous Cream (le VIM amélioré), allez dans le menu <code>Plugin-&gt;Rails-&gt;Projects-&gt;News</code>.</p>


<p><a href="/images/ruby_on_rails/gamestore/001_create_app_rails_gamestore.png" hreflang="fr" title="Création de notre application GameStore grâce à Cream+rails.vim"><img src="/images/ruby_on_rails/gamestore/320x-001_create_app_rails_gamestore.png" alt="mon application" style="display:block; margin:0 auto;" /></a></p>


<p>et par la magie des scripts railesques, on se retrouve avec un répertoire plein de trucs, on fait un petit <code>script/server</code> et dans votre navigateur préféré (firefox biensur ;) ) faites un petit <code>http://localhost:3000/</code> et vous vous retrouverez avec un magnifique&nbsp;:</p>


<p><a href="/images/ruby_on_rails/ide/hello-world-railesque.png"><img src="/images/ruby_on_rails/ide/320x-hello-world-railesque.png.png" alt="Firefox" style="display:block; margin:0 auto;" /></a></p>


<p>Je vous fais grâce d'une description du répertoire type d'une application rails, je vous laisse vous reporter à la litterature que vous trouverez sur le web pour cela. Mais il nous manque une truc essentiel&nbsp;: une base de données&nbsp;!</p>


<p>Donc, comme vous avez déjà un serveur MySQL (<a href="http://www.cheminsdetraverse.info/index.php?2005/12/30/10-environnement-php-partie-13-serveur-mysql" hreflang="fr" title="Pour les développeurs sous Windows, pour les ubuntiens: sudo apt-get install mysql-server">comment ça vous en avez pas un ?</a>), créez une petite base avec comme doux nom <code>gamestore_development</code>.</p>


<pre>mysqladmin -u root -p create gamestore_development</pre>


<p>Connectons notre application Rails à cette base de données en éditant le fichier de configuration de la base (<code>config/database.yml</code>) et modifiez les ligne suivante pour ajuster votre compte/mot de passe et le serveur+port de mysql&nbsp;:</p>


<pre>development:
 adapter: mysql
 database: gamestore_development
 username: root
 password: xxxxxxx
 host: localhost
 port: 3306</pre>


<p>Une fois cela fait, on passe aux choses sérieuses.</p>


<p>Et ensuite, créons une petite table répondant au doux nom de <code>products</code> (attention, le 's' à la fin n'est pas là pour la déco), comportant les champs suivants&nbsp;:</p>

<ul>
<li>id BIGINT</li>
<li>title VARCHAR(255)</li>
<li>description TEXT</li>
<li>image VARCHAR(255)</li>
<li>price</li>
<li>date_available DATETIME</li>
</ul>

<p>Pour cela, nous allons utiliser une des particularité de Ruby et de Rails le <code>script/generate model Product</code></p>


<p>après génération, vous avez toutes une série de fichiers créé, dont deux nous interressent tout particulièrement&nbsp;:</p>
<ul>
<li><code>app/model/product.rb</code></li>
<li>@@db/migration/001_create_products.rb</li>
</ul>

<p>Editons ce deuxième fichier&nbsp;:</p>

<pre>[ruby]
class CreateProducts &lt; ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.column &quot;title&quot;,           :string,	:limit =&gt; 100, :default =&gt; &quot;title&quot;,:null =&gt; false
      t.column &quot;description&quot;,     :text
      t.column &quot;image&quot;,           :string,	:default =&gt; &quot;/images/cover.jpg&quot;,:null =&gt; false
      t.column &quot;price&quot;,           :float,		:default =&gt; -1.0, :null =&gt; false
      t.column &quot;date_available&quot;,  :datetime,	:null =&gt; false
    end
  end

  def self.down
    drop_table :products
  end
end
</pre>


<p>Ainsi, par ce script, et en executant un <code>plugin-&gt;rake-&gt;migrate</code> après l'avoir sauvé, biensur, vous aurez une belle table <code>products</code> dans votre base de données toute neuve.</p>


<p>Une fois tout celà ficelé, retournez dans votre console dans gedit vous faite un petit script/generate scaffold Product store, oubien dans le menu <code>Plugin-&gt;Rails-&gt;Generate-&gt;Scaffold</code>, en précisant dans la fenêtre qui s'ouvre "Product product" et après quelques secondes relancez votre navigateur avec l'adresse @@<a href="http://localhost:3000/product/">http://localhost:3000/product/</a>.</p>


<p>Et magie de Rails, grâce au prophète Ruby vous obtenez une superbe fenêtre au look (moche) qui vous permet de faire toutes les opérations du <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete" hreflang="en">CRUD</a> (non, ce n'est pas un mouvement terroriste...) sur votre liste de produit.</p>


<p>Commençons par en ajouter un, de produit, en utilisant le petit lien "New Product" sur la page affichée. Vous obtenez la page ci-dessous&nbsp;:</p>


<p><a href="/images/ruby_on_rails/gamestore/007_capture-product.png"><img src="/images/ruby_on_rails/gamestore/320x-007_capture-product.png.png" alt="Firefox" style="display:block; margin:0 auto;" /></a></p>


<h3>L'image à télécharger&nbsp;: file_column</h3>


<p>Dans notre modèle de porduit, nous avons un champs judicieusement nommé image. Nous allons lui demander de mémoriser un chemin relatif vers une image que nous uploaderons sur le site.
Pour cela, nous utiliserons le plugin <code>file_column</code> qui va intégrer toute la mécanique d'upload et de stockage d'image.</p>


<p>La commande suivante installera ce plugin&nbsp;:</p>


<pre> script/plugin install http://opensvn.csie.org/rails_file_column/plugins/file_column/trunk</pre>


<p>Dans notre modèle <code>gamestore/app/models/product.rb</code> ajoutons en haut la ligne&nbsp;:</p>
<pre>[ruby]
class Product &lt; ActiveRecord::Base
  file_column :image, 
    :magick =&gt; { :geometry=&gt;&quot;400x600&quot;, 
      :versions=&gt;{ :thumb=&gt; &quot;138x188&quot;, :mini=&gt; &quot;60x90&quot; } }
end
</pre>


<p>Par cette "simple" ligne, nous expliquons à rails qu'il doit passer par la mécanique du plugin pour gérer ce champ et l'image qui va avec: magique, non&nbsp;?
Pour plus d'ionformation sur le charabiat qui suit le paramètre <code>:magick</code>, reportez vous à l'<a href="http://delorme.frederic.free.fr/dotclear/index.php?2007/12/17/285-installation-de-rmagick-sous-ubuntu" hreflang="fr">article concernant RMagick</a> sur mon blog.</p>


<p>Nous allons maintenant enrichir notre interface d'édition de produit.</p>


<p>Pour commencer, éditez le fichier <code>gamestore/app/view/product/new.rhtml</code> et remplacez la ligne</p>


<pre> &lt;% form_tag :action=&gt; 'create' do %&gt;</pre>


<p>par&nbsp;:</p>


<pre> &lt;% form_tag( { :action=&gt; 'create' }, :multipart=&gt; true ) do %&gt;</pre>


<p>Editez ensuite le fichier <code>app/gamestore/views/product/_form.html</code>:</p>


<p>remplacez les lignes</p>

<pre>[ruby]
&lt;p&gt;&lt;label for=&quot;product_image&quot;&gt;Image&lt;/label&gt;&lt;br/&gt;
&lt;%= text_field 'product', 'image'  %&gt;&lt;/p&gt;
</pre>


<p>par ces lignes&nbsp;:</p>

<pre>[ruby]
&lt;p&gt;&lt;label for=&quot;product_image&quot;&gt;Image&lt;/label&gt;
&lt;%= file_column_field 'product', 'image'  %&gt;&lt;/p&gt;
&lt;%if @product.image %&gt;
  &lt;p&gt;&lt;%= image_tag url_for_file_column(&quot;product&quot;, &quot;image&quot;) %&gt;&lt;/p&gt;
&lt;%end%&gt;
</pre>


<p>Et maintenant, appeler l'url&nbsp;: <code><a href="http://localhost:3000/product/new" hreflang="fr">http://localhost:3000/product/new</a></code></p>


<p>Vous pourrez ajouter votre premier produit avec image :) .</p>


<p>Sinon, nous devons légèrement retoucher l'affichage de la liste de produit. Aussi, éditons le fichier <code>gamestore/app/views/product/list.rhtml</code>:</p>


<p>remplacer les lignes auto-générées&nbsp;:</p>

<pre>[ruby]
&lt;table&gt;
  &lt;tr&gt;
  &lt;% for column in Product.content_columns %&gt;
    &lt;th&gt;&lt;%= column.human_name %&gt;&lt;/th&gt;
  &lt;% end %&gt;
  &lt;/tr&gt;
&lt;% for product in @products %&gt;
  &lt;tr&gt;
  &lt;% for column in Product.content_columns %&gt;
    &lt;td&gt;&lt;%=h product.send(column.name) %&gt;&lt;/td&gt;
  &lt;% end %&gt;
    &lt;td&gt;&lt;%= link_to 'Show', :action =&gt; 'show', :id =&gt; product %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;%= link_to 'Edit', :action =&gt; 'edit', :id =&gt; product %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;%= link_to 'Destroy', { :action =&gt; 'destroy', :id =&gt; product }, :confirm =&gt; 'Are you sure?', :method =&gt; :post %&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;% end %&gt;
&lt;/table&gt;
</pre>


<p>par ces lignes&nbsp;:</p>

<pre>[ruby]
&lt;% for @product in @products %&gt;
&lt;tr valign=&quot;top&quot; class=&quot;ListLine &lt;%= cycle('odd','even') %&gt;&quot;&gt;
	&lt;td&gt;		  
		&lt;%= image_tag url_for_file_column(&quot;product&quot;,&quot;image&quot;,&quot;mini&quot;)%&gt;
	&lt;/td&gt;
	&lt;td width=&quot;60%&quot;&gt;
	  &lt;span class=&quot;ListTitle&quot;&gt;&lt;%= h(@product.title) %&gt;&lt;/span&gt;&lt;br /&gt;
	  &lt;%= h(truncate(@product.description, 200)) %&gt;
	&lt;/td&gt;
	&lt;td align=&quot;right&quot;&gt;
	  &lt;%= @product.date_available.strftime(&quot;%y-%m-%d&quot;) %&gt;&lt;br/&gt;
	  &lt;strong&gt;&lt;%= fmt_currency(@product.price) %&gt;&lt;/strong&gt;
	&lt;/td&gt;
	&lt;td class=&quot;ListActions&quot;&gt;
		&lt;%= link_to 'Show', :action =&gt; 'show', :id =&gt; @product %&gt;&lt;br/&gt;
		&lt;%= link_to 'Edit', :action =&gt; 'edit', :id =&gt; @product %&gt;&lt;br/&gt;
		&lt;%= link_to 'Destroy', { :action =&gt; 'destroy', :id =&gt; @product }, :confirm =&gt; &quot;Are you sure?&quot; %&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;% end %&gt;
</pre>


<p>Et donc, en reprenant l'url <code><a href="http://localhost:3000/product/list" hreflang="fr">http://localhost:3000/product/list</a></code>. et voila ce que vous obtenez&nbsp;:</p>


<p><a href="/images/ruby_on_rails/gamestore/008_product_list_avec_image.png"><img src="/images/ruby_on_rails/gamestore/320x-008_product_list_avec_image.png.png" alt="La liste des produits" style="display:block; margin:0 auto;" /></a></p>


<p>Vous pouvez télécharger l'application Rails issue de ce tutorial à cette adresse&nbsp;:</p>
<ul>
<li><a href="http://delorme.frederic.free.fr/dotclear/files/rails/gamestore/rails-gamestore_step_1.tar.gz">http://delorme.frederic.free.fr/dotclear/files/rails/gamestore/rails-gamestore_step_1.tar.gz</a></li>
</ul>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/12/18/35-creer-une-boutique-en-ligne-partie-1-choix-des-outils">
  <title>Créer une boutique en ligne avec Rails - Partie 1 : Choix des outils</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/12/18/35-creer-une-boutique-en-ligne-partie-1-choix-des-outils</link>
  <dc:date>2007-12-18T16:22:37+01:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>McGivrer</dc:creator>
  <dc:subject>Ruby on Rails</dc:subject>
  <description>Après l'achat du livre "Agile Web development with Rails", vous ne pourrez plus lâcher Rails, essayant d'aller toujours plus loin dans ses arcanes ! Aussi, je vous propose de réaliser une petite application d'achat en ligne inspirée de celle proposée dans le livre, mais en ajoutant plusieurs notions, comme, une gestion de catégories pour organiser les produits proposés à la vente, une gestion d'utilisateurs, la gestion des commandes, de leur traitement et de leur suivis. mais commençons par le commencement, des outils de développement multi-plateforme (tant qu'à faire)...</description>
  <content:encoded><![CDATA[<p>Après l'achat du livre "Agile Web development with Rails", vous ne pourrez plus lâcher Rails, essayant d'aller toujours plus loin dans ses arcanes&nbsp;! Aussi, je vous propose de réaliser une petite application d'achat en ligne inspirée de celle proposée dans le livre, mais en ajoutant plusieurs notions, comme, une gestion de catégories pour organiser les produits proposés à la vente, une gestion d'utilisateurs, la gestion des commandes, de leur traitement et de leur suivis. mais commençons par le commencement, des outils de développement multi-plateforme (tant qu'à faire)...</p> <h3>IDE ou éditeur&nbsp;?</h3>


<p>Après avoir tester bien des solutions, j'ai fini par en retenir 2 qui sont "vivables", à savoir&nbsp;:</p>
<ul>
<li>Gedit avec quelques plugins</li>
<li><a href="http://cream.sourceforge.net/" hreflang="en">Cream</a> + <a href="http://www.vim.org/scripts/script.php?script_id=1567" hreflang="en">Rails.vim</a>, qui est la <a href="http://www.cheminsdetraverse.info/index.php?2006/12/04/17-ruby-on-rails-vim" hreflang="fr">solution</a> que j'utilise aujourd'hui, et que je préfère. ayant commencé par Vim, j'ai découvert Cream qui est bien plus "Windows" ou "Gnome" friendly&nbsp;!</li>
</ul>

<p>Qui plus est l'intéret de cet éditeur est qu'il est multi-plateforme, et donc tourne aussi bien</p>


<p>J'ai également testé Eclipse + RadRails, et AptanaStudio 1.0 (en version Community) mais rien y fait, la "lourdeur" de l'ide Eclipse correspond certainement à un langage comme Java/J2EE, mais pas à la souplesse de Rails&nbsp;! En effet, il faut pouvoir jongler rapidement avec les fichiers, avec les scripts des générateurs rails, avec une console irb / bash et avec un navigateur.</p>


<h4>Cream + Rails vim script</h4>


<p><a href="/images/ruby_on_rails/ide/editor_cream-rails.png" title="GVim avec le script railsvim qui sauve la vie des railers" rel="lightbox"><img src="/images/ruby_on_rails/ide/320x-editor_cream-rails.png" alt="Vim avec LE plugin Rails" style="display:block; margin:0 auto;" /></a></p>


<p>Et donc, Cream qui est une évolution de Vim équipé du script rails.vim devient une bombe à utiliser et a mon avis s'approche fortement au point de vue ergonomie du fameux TextMate sur Mac OS X. Sa puissance réside dans les scripts Rails qui sont hautement intégrés dans les menus de Cream. Ils vous permettront (ces scripts...) d'accéder à l'ensemble des générateurs du framework, de piloter le serveur Webrick, mais également de piloter les migrations via le "ruby make" (Rake) et donc d'effectuer toutes les operations sur la base de données.</p>


<h4>Gedit, l'éditeur natif de Gnome</h4>


<p><a href="/images/ruby_on_rails/ide/ide_rails_gedit.png" rel="lightbox"><img src="/images/ruby_on_rails/ide/320x-ide_rails_gedit.png" alt="Gedit, l&#039;éditeur Gnome" style="display:block; margin:0 auto;" /></a></p>



<p>Gedit, pour les connaisseurs de Linux et plus particulièrement d'Ubuntu, je ne ferais pas l'affront de le présenter, aussi passez au chapitre suivant. Pour les autres,  c'est l'éditeur fournit par défaut dans le gestionnaire de fenêtre GNOME. Mais, ici, Gedit + word completion plugin + snippets plugin + synthaxe colorization "TextMate" mode sont les atouts que nous exploitons dans le cadre de notre environnement de développement.</p>


<h4>Et pour la preview&nbsp;?</h4>


<p><a href="/images/ruby_on_rails/ide/mozilla-firefox.png" rel="lightbox"><img src="/images/ruby_on_rails/ide/320x-mozilla-firefox.png" alt="Firefox, l&#039;outil magique" style="display:block; margin:0 auto;" /></a></p>


<p>Nous parlions "navigateur web", aussi je me dois de vous conseiller l'utilisation du couple Firefox + Firebug pour pouvoir débugger HTML, CSS et javascript avec une facilité toute déconcertante. Firebug vous permettra même d'identifier certains goulots d'étranglement dans le déroulement de la génération de vos pages.</p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/09/05/34-extensions-firefox-pour-developper-vos-sites-web">
  <title>Extensions Firefox pour développer vos sites Web</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/09/05/34-extensions-firefox-pour-developper-vos-sites-web</link>
  <dc:date>2007-09-05T10:31:28+02:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>Ashesheart</dc:creator>
  <dc:subject>DevWeb</dc:subject>
  <description>Le développement Web est de plus en plus avancée aujourd'hui avec les nouvelles technologies du Web 2.0. Qui n'a jamais songé à developper son blog ou son site Web voir se lancer dans le développement Web à plus grande échelle? Cet article vous présente les extensions Firefox indispensables...</description>
  <content:encoded><![CDATA[ <p>Le développement Web est de plus en plus avancée aujourd'hui avec les nouvelles technologies du Web 2.0. Qui n'a jamais songé à developper son blog ou son site Web voir se lancer dans le développement Web à plus grande échelle? Cet article vous présente les extensions Firefox indispensables pour adapter votre site aux normes et standards ainsi que pour débugguer celui-ci en cas de problème.</p>


<p>Tout d'abord une extension très connue est <strong>la Web Developper Tool Bar</strong>. Cette extension est doté de fonctionnalités vraiment intéressantes:</p>
<ul>
<li>Redimensionnement de la fenêtre: avec cette fonctionnalité vous pouvez vérifiez que votre site/blog fonctionne sur plusieurs résolutions.</li>
<li>Voir les CSS: vérifiez votre code CSS pour analyser les éventuels problèmes.</li>
<li>Valider CSS et HTML: mettez votre site aux normes afin d'en améliorer l'accessibilité et le référencement.</li>
<li>etc.</li>
</ul>

<p>Cette extension possède de nombreuses autre fonctionnalités que je vous laisse découvrir par vous même.</p>


<p>La seconde extension est <strong>Firebug</strong>: Puissant outil de débuggage, il vous permet d'analyser le code javascript, html, css, etc. Vous pouvez également ajouter des espions sur votre code source afin de trouver les éventuelles erreurs plus facilement. C'est certainement l'une des meilleures extensions à ce jour.</p>


<p>Enfin la troisième extension que je vous conseille est <strong>IEtab</strong>: si vous souhaitez que votre site soit compatible Firefox et IE, je vous recommande l'installation de ce plugin. Il vous permet en un clic de lancer la page en cours sur un nouvel onglet avec le moteur d'internet explorer. Un autre clic relance la page sous le moteur de Firefox. Extension indispensable si vous souhaitez rendre votre site le plus compatible possible avec les navigateurs les plus utilisés.</p>


<p>Pour finir ce billet je vous laisse quelques liens ou vous trouverez ces extensions:</p>
<ul>
<li>Web Developper Tool Bar: https://addons.mozilla.org/fr/firefox/addon/60</li>
<li>Firebug: https://addons.mozilla.org/fr/firefox/addon/1843</li>
<li>IE Tab: https://addons.mozilla.org/fr/firefox/addon/1419</li>
</ul>

<p>Ceci n'est pas la liste exhaustive d'extensions qui vous permettra de développer vos sites Web. Néanmoins, avec ces trois extensions vous arriverez déjà à développer des sites efficacement.</p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/08/07/33-doxygen-et-l-integration-continue">
  <title>Doxygen et l'intégration continue ...</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/08/07/33-doxygen-et-l-integration-continue</link>
  <dc:date>2007-08-07T18:52:57+02:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>Wiz</dc:creator>
  <dc:subject>J2EE</dc:subject>
  <description>Plugins maven, tâche ant, tout semble être là pour intégrer Doxygen dans un processus d'intégration continue ...


Et pourtant !</description>
  <content:encoded><![CDATA[<p>Plugins maven, tâche ant, tout semble être là pour intégrer Doxygen dans un processus d'intégration continue ...</p>


<p>Et pourtant&nbsp;!</p> <p>A priori pas facile, en tout cas j'ai eu le temps de me poser quelques questions sur le sujet.</p>


<p>En effet, on constate assez rapidement l'existence d'un plugin maven (peut être même 2) pour utiliser <a href="www.doxygen.org" hreflang="en">doxygen</a> (qui rappelons permet de faire une jolie documentation dans la lignée de javadoc, mais sans comparaison possible !). Pour les plugins, pas moyen de les faire fonctionner. Ils datent un peu ... ça y est peut être pour quelque chose (ou alors c'est moi !)</p>


<p>On change son fusil d'épaule, on trouve alors une tâche ant (<a href="http://ant-doxygen.sourceforge.net/usage.php" hreflang="en">ant-doxygen-1.4</a>), qui devrait fonctionner elle. Ben ... non (ou c'est encore moi, mais la ça fait beaucoup quand même).</p>


<p>Bon j'abrège un peu, après m'être dit que j'allais le faire moi même ce plugin, une dernière idée à tester me vint à l'esprit. Il devait bien exister une tâche lançant un executable&nbsp;? Eureka, ça existe&nbsp;: au final la solution tiens dans cette ligne&nbsp;: <code>&lt;exec dir="." executable="doxygen" /&gt; </code><br /></p>


<p>Je n'ai pas mis de paramètre, car la commande doxygen utilise le fichier Doxyfile qui se trouve dans le répertoire courant. Fichier que l'on peut facilement génerer et modifier à sa guise en utilisant Doxywizard.</p>


<p>Quand je pense à tout ce temps perdu&nbsp;!</p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/07/19/32-je-me-presente-je-m-appelle-remi">
  <title>Je me présente je m'appelle Rémi !</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/07/19/32-je-me-presente-je-m-appelle-remi</link>
  <dc:date>2007-07-19T09:47:25+02:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>Ashesheart</dc:creator>
  <dc:subject>PHP</dc:subject>
  <description>Bonjour à toutes et à tous,


La création de site web est pour moi une vraie passion. Selon moi php+css+javascript sont les meilleures armes pour faire un excellent site respectant les standards.


A bientôt pour des articles passionnants!...</description>
  <content:encoded><![CDATA[ <p>Bonjour à toutes et à tous,</p>


<p>La création de site web est pour moi une vraie passion. Selon moi php+css+javascript sont les meilleures armes pour faire un excellent site respectant les standards.</p>


<p>A bientôt pour des articles passionnants!</p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/07/19/31-nouvelles-recrues">
  <title>Nouvelles recrues !</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/07/19/31-nouvelles-recrues</link>
  <dc:date>2007-07-19T09:41:57+02:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>McGivrer</dc:creator>
  <dc:subject>General</dc:subject>
  <description>Merci à nos deux nouveaux collaborateurs Ashesheart et Wizards63 qui rejoignent l'équipe de rédaction du site.


Nous leur souhaitons la bienvenue et une bonne plume pour la rédaction de nombreux billets ;)


McG....</description>
  <content:encoded><![CDATA[ <p>Merci à nos deux nouveaux collaborateurs Ashesheart et Wizards63 qui rejoignent l'équipe de rédaction du site.</p>


<p>Nous leur souhaitons la bienvenue et une bonne plume pour la rédaction de nombreux billets ;)</p>


<p>McG.</p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/06/02/30-databinding-swt-text-eclipse-33">
  <title>Databinding SWT Text: Eclipse 3.3</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/06/02/30-databinding-swt-text-eclipse-33</link>
  <dc:date>2007-06-02T11:12:13+02:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>tatemilio</dc:creator>
  <dc:subject>Java</dc:subject>
  <description>Voici un post permettant d'effectuer un bind entre des objets graphiques (org.eclipse.swt.widgets.Text) et un objet métier.
Imaginons une vue permettant de créer un utilisateur. Cette vue contient 2 zones Text permettant de saisir le nom et le prénom de l'utilisateur et un bouton de création....</description>
  <content:encoded><![CDATA[ <p>Voici un post permettant d'effectuer un bind entre des objets graphiques (org.eclipse.swt.widgets.Text) et un objet métier.<br />
Imaginons une vue permettant de créer un utilisateur. Cette vue contient 2 zones Text permettant de saisir le nom et le prénom de l'utilisateur et un bouton de création.</p>


<p>L'utilisateur est modélisé par une classe métier qui peut ressembler à ça:</p>
<pre>[java]
public class Utilisateur{
  // Nom de l'utilisateur
  private String nom;
  // Prénom de l'utilisateur
  private String prenom;
  // Getters Setters
  public String getNom() {
    return nom;
  }
  public void setNom(String nom {
    this.nom = nom
  }
  public String getPrenom() {
    return prenom;
  }
  public void setPrenom(String prenom {
    this.prenom = prenom
  }
}
</pre>


<p>Jusque là rien d'extraordinaire, passons maintenant à la création de l'utilisateur par l'intermédiaire de la vue RCP.
Lorsque le nom et le prénom sont renseignés, il faut clicquer sur créer, dès lors il y a deux possibilités:</p>


<p>1) Instancier la classe utilisateur et "peupler" les attributs nom et prénoms manuellement en récupérant les valeurs saisies et utliser les setters.</p>


<p>2) utiliser le Databing pour que les attributs de la classe utilisateur soient peuplés automatiquement.</p>


<p>Voici le code qui permet de le faire:</p>

<pre>[java]
Utilisateur utilisateur = new Utilisateur();

DataBindingContext db = new DataBindingContext();
Realm realm = SWTObservables.getRealm(display);
// Nom de l'utilisateur
db.bindValue(SWTObservables.observeText(txtNom,SWT.Modify), BeansObservables.observeValue(realm, utilisateur, &quot;nom&quot;), null, null);
// Prenom de l'utilisateur
db.bindValue(SWTObservables.observeText(txtPrenom,SWT.Modify), BeansObservables.observeValue(realm, utilisateur, &quot;prenom&quot;), null, null);
</pre>


<p>Je n'ai pas tout détailler au niveau de la vue RCP, il faudra adapter ce code pour l'utiliser dans vos applications néanmoins le principal est là.</p>


<p>Dernière remarque et de taille, le bind est bi-directionnel cela signifie que si vous alimentez les attributs de l'objet métiers les objets graphiques prendront les valeurs définies.</p>]]></content:encoded>
</item>
<item rdf:about="http://www.cheminsdetraverse.info/index.php?2007/04/20/29-eclipse-plugin-tableau-de-proprietes">
  <title>Eclipse plugin: Tableau de propriétés</title>
  <link>http://www.cheminsdetraverse.info/index.php?2007/04/20/29-eclipse-plugin-tableau-de-proprietes</link>
  <dc:date>2007-04-20T10:19:14+02:00</dc:date>
  <dc:language>fr</dc:language>
  <dc:creator>McGivrer</dc:creator>
  <dc:subject>J2EE</dc:subject>
  <description>Ou comment gérer les valeurs d'un tableau de propriétés dans la page de préférence d'un plugin Eclipse ?</description>
  <content:encoded><![CDATA[<p>Ou comment gérer les valeurs d'un tableau de propriétés dans la page de préférence d'un plugin Eclipse&nbsp;?</p> <p>Donc pour tout sauvegarder j'utilise bien en fait les préférences. c'est à dire l'interface IPreferenceStore.</p>


<p>Comment je j'utilise&nbsp;?</p>


<p>Exemple sur une table&nbsp;:</p>


<p>il s'agit d'un TableViewer par défaut pour mettre des tableaux dans un éditeur</p>
<pre>java
public abstract class TableViewerForEditors extends Composite{

private Table tab;
protected TableViewer tbv;

private ListenerControl leListenerControlTac;
private ListenerSelection leListenerSelectionTac;

protected IPreferenceStore lesPrefs;

/**
* @param parent
* @param style
* @param input
*/
public TableViewerForEditors(Composite parent, int style, Object input) {
super(parent,style);
this.setLayout(new FillLayout());
leListenerControlTac = new ListenerControl(this);
leListenerSelectionTac = new ListenerSelection(this);

createTable();

tbv = new TableViewer(tab);

createColumns();

configureViewer();

tbv.setInput(input);


miseEnForme(lesPrefs);

//Ajout des listeners
for(TableColumn tac : tab.getColumns()){
tac.addSelectionListener(this.leListenerSelectionTac);
tac.addControlListener(this.leListenerControlTac);
}


if (lesPrefs != null){
String nomClasse = getClass().getSimpleName();
String temoin = lesPrefs.getString(nomClasse + PreferencesCst.TEMOIN);
String sortKey = nomClasse+ PreferencesCst.SEP_POINT;
sortKey += PreferencesCst.SORT;
//Mise en place des tailles par défaut
if (!temoin.equalsIgnoreCase(PreferencesCst.OK)){
for(TableColumn tac : tab.getColumns()){
tac.setWidth(tac.getWidth());
}
String nomColonne = tab.getSortColumn().getText();
lesPrefs.setValue(sortKey, nomColonne);
//Mise en place de la colonne trié
}else{
String nomColonne = lesPrefs.getString(sortKey);
for(TableColumn tac : tab.getColumns()){
if (tac.getText().equalsIgnoreCase(nomColonne)){
tab.setSortColumn(tac);
}
}
}

}
}

/**
* @param lesPrefs
* Méthode permettant d'appliquer la dernière mise en forme de la vue
*/
protected void miseEnForme(IPreferenceStore lesPrefs){
if (lesPrefs != null){
String nomClasse = getClass().getSimpleName();
String temoin = lesPrefs.getString(nomClasse + PreferencesCst.TEMOIN);
if (temoin.equalsIgnoreCase(PreferencesCst.OK)){
int place = 0;
int size = 0;
String param = PreferencesCst.EMPTY;
String paramPlace = PreferencesCst.EMPTY;
String paramSize = PreferencesCst.EMPTY;
int[] order = new int[tab.getColumnCount()];
int i = 0;
//Mise en place de l'ordre des colonnes
for(TableColumn tac : tab.getColumns()){
param = nomClasse + PreferencesCst.SEP_POINT;
param += tac.getText();
paramPlace = param
+ PreferencesCst.SEP_POINT
+ PreferencesCst.PLACE;
paramSize = param
+ PreferencesCst.SEP_POINT
+ PreferencesCst.SIZE;
place = lesPrefs.getInt(paramPlace);
order[i] = place;
size = lesPrefs.getInt(paramSize);
tac.setWidth(size);
i++;
}
tab.setColumnOrder(order);
}
}
}

/**
* Méthode créant la table
*/
private void createTable(){
tab = new Table(this, SWT.NONE);
// GridData gridData = new GridData(GridData.FILL_BOTH);
// gridData.grabExcessVerticalSpace = true;
// tab.setLayoutData(gridData);
tab.setLinesVisible(true);
tab.setHeaderVisible(true);
}

/**
* &lt;p&gt;Méthode servant à créer toutes les colonnes de la table.&lt;/p&gt;&lt;br&gt;
*
* &lt;p&gt;à implémenter dans toute classe héritant de TableViewerForEditors.&lt;/p&gt;
*/
protected abstract void createColumns();

/**
* &lt;p&gt;Méthode servant à instancier le content, le label ainsi que
* le fichier de préférences.&lt;/p&gt; &lt;br&gt;
*
* &lt;p&gt;à implémenter dans toute classe héritant de TableViewerForEditors.&lt;/p&gt;
*/
protected abstract void configureViewer();

/**
* @param text
* @param tooltip
* @param style
* @param sorter
* @param initialDirection
* @param keepDirection
* @param autoPack
*
* &lt;p&gt; Sert à créer une colonne dans la table&lt;/p&gt;&lt;br&gt;
* @return
*/
public TableSortSelectionListener createTableColumn(
String text,
String tooltip,
int style,
AbstractInvertableTableSorter sorter,
int initialDirection,
boolean keepDirection,
boolean autoPack)
{
return createTableColumn(text, tooltip, style, sorter, initialDirection, keepDirection, autoPack, 50);
}

/**
* @param text
* @param tooltip
* @param style
* @param sorter
* @param initialDirection
* @param keepDirection
* @param autoPack
* @param width
*
* &lt;p&gt; Sert à créer une colonne dans la table&lt;/p&gt;&lt;br&gt;
* @return
*/
public TableSortSelectionListener createTableColumn(
String text,
String tooltip,
int style,
AbstractInvertableTableSorter sorter,
int initialDirection,
boolean keepDirection,
boolean autoPack,
int width)
{
TableColumn column = new TableColumn(this.tab, style);
column.setText(text);
column.setToolTipText(tooltip);
column.setResizable(true);
column.setMoveable(true);
column.setWidth(width);
return new TableSortSelectionListener(this.tbv, column, sorter,initialDirection, keepDirection, autoPack);
}

/**
* @param text
* @param tooltip
* @param style
*
* &lt;p&gt; Sert à créer une colonne dans la table&lt;/p&gt;&lt;br&gt;
* @return
*/
public TableColumn createTableColumn(
String text,
String tooltip,
int style)
{
return createTableColumn(text,tooltip,style,50);
}

/**
* @param text
* @param tooltip
* @param style
* @param width
*
* &lt;p&gt; Sert à créer une colonne dans la table&lt;/p&gt;&lt;br&gt;
* @return
*/
public TableColumn createTableColumn(
String text,
String tooltip,
int style,
int width)
{
TableColumn column = new TableColumn(tab, style);
column.setText(text);
column.setToolTipText(tooltip);
column.setWidth(width);
TableSorter sorter2 = new TableSorter(tbv.getTable().indexOf(column));
@SuppressWarnings(&quot;unused&quot;)
TableSortSelectionListener sortListener = new TableSortSelectionListener(tbv, column, sorter2,SWT.UP, false, true);
return column;
}

/**
* @return
*/
public Table getTab() {
return tab;
}

/**
* @param tab
*/
public void setTab(Table tab) {
this.tab = tab;
}

/**
* @return
*/
public TableViewer getTbv() {
return tbv;
}

/**
* @param tbv
*/
public void setTbv(TableViewer tbv) {
this.tbv = tbv;
}

/**
* @return
*/
public IPreferenceStore getLesPrefs() {
return lesPrefs;
}



}
</pre>




<p>PreferencesCst.java&nbsp;:</p>
<pre>java

public abstract class PreferencesCst {

public static String TEMOIN = &quot;temoin&quot;;
public static String OK = &quot;OK&quot;;
public static String EMPTY = &quot;&quot;;
public static String SEP_POINT = &quot;.&quot;;
public static String PLACE = &quot;place&quot;;
public static String SIZE = &quot;size&quot;;
public static String SORT = &quot;sort&quot;;

}
</pre>


<p>ListenerSelection.java&nbsp;:</p>
<pre>java

//ListenerDefault est une super classe dans laquelle on a juste un paramètre
//l'objet contenant le listener
public class ListenerSelection extends ListenerDefault implements
SelectionListener {


public ListenerSelection(Object lAppelant) {
super(lAppelant);
}

/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent e) {
if(getLAppelant() instanceof TableViewerForEditors){
TableViewerForEditors laVue = (TableViewerForEditors)getLAppelant();
if (laVue.getLesPrefs() != null){
String nomClasse = getLAppelant().getClass().getSimpleName();
laVue.getLesPrefs().setValue(nomClasse + PreferencesCst.TEMOIN, PreferencesCst.OK);
TableColumn tac = (TableColumn)e.getSource();
String param = nomClasse + PreferencesCst.SEP_POINT;
param += PreferencesCst.SORT;
laVue.getLesPrefs().setValue(param, tac.getText());
}
}

}

/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e) {
if(getLAppelant() instanceof TableViewerForEditors){
TableViewerForEditors laVue = (TableViewerForEditors)getLAppelant();
if (laVue.getLesPrefs() != null){
String nomClasse = getLAppelant().getClass().getSimpleName();
laVue.getLesPrefs().setValue(nomClasse + PreferencesCst.TEMOIN, PreferencesCst.OK);
STableColumn tac = (STableColumn)e.getSource();
String param = nomClasse+ PreferencesCst.SEP_POINT;
String paramSort = param + PreferencesCst.SORT;
laVue.getLesPrefs().setValue(paramSort, tac.getText());
}
}
}

}
</pre>


<p>ListenerControl.java&nbsp;:</p>
<pre>java

public class ListenerControl extends ListenerDefault implements
ControlListener {

public ListenerControl(Object lAppelant) {
super(lAppelant);
}

/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
*/
public void controlMoved(ControlEvent e) {
if (super.getLAppelant() instanceof TableViewerForEditors){
TableViewerForEditors laVue = (TableViewerForEditors)getLAppelant();
if (laVue.getLesPrefs() != null){
String nomClasse = getLAppelant().getClass().getSimpleName();
laVue.getLesPrefs().setValue(nomClasse + PreferencesCst.TEMOIN, PreferencesCst.OK);
STableColumn tac = (TableColumn)e.getSource();
String param = PreferencesCst.EMPTY;
int i = 0;
for(int order : tac.getParent().getColumnOrder()){
param = nomClasse + PreferencesCst.SEP_POINT;
param += tac.getParent().getColumn(i).getText()+PreferencesCst.SEP_POINT;
param += PreferencesCst.PLACE;
laVue.getLesPrefs().setValue(param, order);
i++;
}
}


}
}

/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
*/
public void controlResized(ControlEvent e) {
if (super.getLAppelant() instanceof TableViewerForEditors){
TableViewerForEditors laVue = (TableViewerForEditors)getLAppelant();
if (laVue.getLesPrefs() != null){
String nomClasse = getLAppelant().getClass().getSimpleName();
laVue.getLesPrefs().setValue(nomClasse + PreferencesCst.TEMOIN, PreferencesCst.OK);
TableColumn tac = (TableColumn)e.getSource();
String param = nomClasse + PreferencesCst.SEP_POINT;
param += tac.getText()+PreferencesCst.SEP_POINT;
param += PreferencesCst.SIZE;
laVue.getLesPrefs().setValue(param, tac.getWidth());
}
}
}

}
</pre>




<p>Maintenant, voici comment s'implémente un objet TableViewerForEditors</p>

<pre>java
public class MonTableViewer extends TableViewerForEditors {

public MonTableViewer(Composite parent, int style, Object input) {
super(parent, style, input);
}


@Override
protected void createColumns() {
createTableColumn(&quot;col1&quot;, &quot;col1&quot;,SWT.LEFT, new MonSorter(&quot;col1&quot;), SWT.UP, true, true).chooseColumnForSorting();
createTableColumn(&quot;col2&quot;, &quot;col2&quot;, SWT.LEFT, new MonSorter(&quot;col2&quot;), SWT.UP, false, true);
createTableColumn(&quot;col3&quot;, &quot;col3&quot;, SWT.LEFT, new MonSorter(&quot;col3&quot;), SWT.UP, false, true);
createTableColumn(&quot;col4&quot;, &quot;col4&quot;, SWT.LEFT, new MonSorter(&quot;col4&quot;), SWT.UP, false, true);
createTableColumn(&quot;col5&quot;, &quot;col5&quot;, SWT.LEFT, new MonSorter(&quot;col5&quot;), SWT.UP, false, true);
}

@Override
protected void configureViewer() {
lesPrefs = MonActivator.getDefault().getPreferenceStore();
tbv.setContentProvider(new MonContentProvider());
tbv.setLabelProvider(new MonLabelProvider());
}

}
</pre>



<p>il suffit juste ensuite de d'instancier un objet MonTableViewer dans éditeur pour que ça marche.</p>





<p>Pour la sauvegarde des préférences au niveau de la perspective, il faut aller dans les classes suivantes&nbsp;:</p>


<p>ApplicationWorkbenchWindowAdvisor.java</p>


<p>il faut ajouter la méthode suivante</p>

<pre>java
/* (non-Javadoc)
* @see org.eclipse.ui.application.WorkbenchWindowAdvisor#preWindowShellClose()
*/
@SuppressWarnings(&quot;finally&quot;)
@Override
public boolean preWindowShellClose() {
try{
String idPerspective = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getPerspective().getId();
PlatformUI.getPreferenceStore().setValue(&quot;LastPerspective&quot;, idPerspective);
IWorkbenchWindow[] lesWindows = PlatformUI.getWorkbench().getWorkbenchWindows();
int i = 0;
for (IWorkbenchWindow leWorkbench : lesWindows){
i++;
IWorkbenchPage[] lesPages = leWorkbench.getPages();
for (IWorkbenchPage laPage : lesPages){
i++;
laPage.savePerspective();
}
}
}catch (Exception e){
e.printStackTrace();
}finally{
return super.preWindowShellClose();
}
}
</pre>



<p>dans la classe suivante&nbsp;: ApplicationWorkbenchAdvisor.java</p>

<pre>java
public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {

private static String PERSPECTIVE_ID = &quot;chemin.perspective&quot;;


public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
return new ApplicationWorkbenchWindowAdvisor(configurer);
}

public String getInitialWindowPerspectiveId() {

String laPerspectivePreference = PlatformUI.getPreferenceStore().getString(&quot;LastPerspective&quot;);
if (!(laPerspectivePreference == null)&amp;&amp;!(&quot;&quot;.equals(laPerspectivePreference))){
perspectiveID = laPerspectivePreference;
}
//return PERSPECTIVE_ID;
return perspectiveID;
}
}
</pre>



<p>Voilà, je crois avoir tout ajouté :)</p>


<p>Après pour sauvegarder ce qu'on veut, il faut utiliser le fichier de préférence comme ci dessus</p>]]></content:encoded>
</item>

</rdf:RDF>
