Articles

Comment gérer les utilisateurs et leurs rôles avec une liaison has_one

Je me suis arraché les cheveux pour trouver la bonne manière de gérer un cas d’utilisation relativement courant dans une application web : ayant un modèle (et une table) User, je souhaitais pouvoir affecter un utilisateur à chaque modèle Account (stocké dans une table … accounts) en tant que Manager. Bien entendu, je souhaitais pour voir accéder à mon manager en faisant :

a = Account.first
a.manager = User.first

En fait c’est très simple, en utilisant les options disponibles dans les associations. On aura donc dans le modèle Account une association belongs_to :

class Account < ActiveRecord::Base
  belongs_to :manager, :class_name => 'User'
end

Au lieu de faire simplement un belongs_to :user, on indique à Rails le modèle qui doit être utilisé.
Dans le modèle User, on aura l’association inverse :

class User < ActiveRecord::Base
  has_one :account, :foreign_key => 'manager_id'
end

Afin que Rails puisse retrouver l’utilisateur à partir de la table accounts, il faut bien sûr désigner la colonne qui va héberger l’identifiant de l’utilisateur grâce au paramètre :foreign_key.

Vous aurez bien entendu ajouté cette colonne à la table accounts en créant une migration :

rails g migration AddManagerIdToAccount

Qui contiendra le code suivant :

class AddManagerIdToAccounts < ActiveRecord::Migration
  def change
    add_column :accounts, :manager_id, :integer
  end
end

Vous pouvez ensuite exécuter la migration, et  le tour est joué !

jQuery Mobile 2e partie : les vues

Le précédent article de notre série sur jQuery Mobile annonçait une suite sur la gestion des vues dans jQuery. Je gère l’affichage des contacts avec un Partial qui est inséré dans la vue elle-même. On a donc la hiérarchie suivante :

Layout => Vue => Partial

Nous avons vu dans l’article précédent l’architecture du Layout, et une partie de la vue elle-même,dont le code complet est donné ci-dessous :

<% content_for :back do %><%= link_to t(:"general.back"),  params[:category] ? categories_path : root_path, :'data-direction'=>"reverse", :'data-icon'=>"arrow-l"  %><% end %>
<% content_for :title do %><%= params[:category] ? Category.find(params[:category]).name : t(:"contact.all") %><% end %>
<% content_for :button do %> <%= link_to t(:"contact.add"), new_contact_path, :'data-icon'=>"add", :rel => "external" %> <% end %>
<%= render "contacts_list" %>
Dans jQM, l'architecture d'une liste est la suivante :
<ul data-role="listview">
	<li>Item 1</li>
	<li>Item 2</li>
	<li>Item 3</li>
</ul>

L’adjonction du data-role « listview » à une combinaison classique ul/li permet de générer une liste dont le rendu est similaire à celui trouvé sur les applications mobiles.

 

 

Le code source du Partial est le suivant :

<ul id="contactslist" data-role="listview"  data-filter="true" data-filter-placeholder="Filtrer la liste..." data-inset="true" class="separator">
  <% @contacts.each do |contact| %>
      <li class="contact"><%= link_to  contact.listname, contact %></li>
    <% end %>
</ul>

Les paramètres passés dans le tag <ul> sont les suivants :

  • data-role : doit être égal à « listview » pour être reconnu par jQM comme une liste.
  • data-filter : si positionné à TRUE, permet d’afficher un champ de filtre au-dessus de la liste ; ne filtre que les données existant dans la liste, alors attention à la pagination !
  •  data-filter-placeholder : permet de modifier le texte par défaut  affiché dans le champ de filtre.
  • data-inset : si positionné à TRUE, permet d’avoir une liste qui ne s’étend pas d’un bord à l’autre de la fenêtre, mais qui laisse un espace sur les bords de la liste ; la première et la dernière cellule voient de plus leurs bords s’arrondir.

Dans le prochain article, nous verrons la création de formulaires avec jQM. D’ici là, n’hésitez pas à laisser vos commentaires !

Développer pour les plateformes mobiles avec jQuery Mobile 1ère partie

Je me suis remis à Rails voici quelque temps en développant un projet de carnet d’adresses. La version « classique » fonctionnait relativement bien, mais j’avais envie de proposer également un rendu spécifique pour les plateformes mobiles. Utilisant déjà jQuery dans mon application, je suis allé voir ce qu’ils proposaient pour « mobilifier » une application Web.

Dans un premier temps, alléché par le screencast de Ryan Bates sur le sujet, je me suis tourné vers jQTouch, qui ciblait les appareils iOS. Mis à part quelques petits soucis au niveau des boutons « Back », ce plugin remplissait correctement son rôle.

Mais voici quelques semaines, je suis tombé sur jQuery Mobile, le plugin « officiel » qui remplissait la même fonction tout en étant plus complet en ce qui concerne les plateformes supportées. Cela se fait au détriment d’une apparence moins centrée sur l’iPhone/iPad, mais le jeu en vaut la chandelle.

Structure d’une page

Je ne reviens pas sur la mise en place d’une application mobile, consultez le screencast de Ryan Bates pour plus d’information. La mise en place de jQuery Mobile est assez similaire à celle de jQTouch ; on modifie le markup des fichiers  mobile.erb, en commençant par le layout principal :

<!DOCTYPE html>
<html>
<head>
  <title><%= t(:"general.title") %></title>

  <%= stylesheet_link_tag "/jquery.mobile/jquery.mobile-1.0b2.min.css" %>
  <%= javascript_include_tag 'jquery-1.6.2.min.js' , :charset => "utf-8" %>
  <%= javascript_include_tag '/jquery.mobile/jquery.mobile-1.0b2.min.js' , :charset => "utf-8" %>
  <%= javascript_include_tag 'jquery.scrollExtend.min' %> 	

  <%= csrf_meta_tag %>
  <%= yield(:head) %>
</head>
 <body>
  <div data-role="page"  >
    <div data-role="header" data-theme="b" >
      <%= yield :back %>
      <h1><%= yield :title %></h1>
      <%= yield :button %>
    </div>
    <div data-role="content">
      <% unless flash.empty? %>
        <div>
          <%- flash.each do |name, msg| -%>
            <%= content_tag :div, msg, :id => "flash_#{name}" %>
          <%- end -%>
        </div>
      <% end %>
      <%= yield %>
    </div>
<div style="text-align:center;font-size:12px;color:grey;" ><%= t(:"general.title") %> <%= "v"+APPLICATION_VERSION %> - <%= raw t(:"general.copyright") %></div>
  </div>
 </body>
</html>

J’ai choisi pour des raisons de commodité de développement d’insérer les fichiers .js de jQuery Mobile directement dans mon projet, mais vous pouvez bien sûr les laisser pointer vers les adresses officielles.

La structure d’une page jQM est relativement simple :

<div data-role="page"  >
  <div data-role="header" data-theme="b" >
  </div>
  <div data-role="content">
  </div>
  <div data-role="footer">
  </div>
</div>

Une « page » contient un en-tête (header), du contenu (content) et un pied de page (footer). Je ne me suis servi que du header et du content pour ce projet, comme vous le constatez dans mon code. Il est également possible d’avoir plusieurs pages dans le même fichier, en leur donnant des ids différents, mais celà ne cadre pas avec la philosophie de Rails.

Un en-tête variable pour vos pages avec yield

Sur la plupart des plateformes mobiles, l’en-tête de la page est réservée à la navigation et aux actions de l’application. Cette partie est hautement variable, puisqu’elle va dépendre de l’action que l’utilisateur est en train de réaliser.

Choix du filtrePar catégories

Comme vous le voyez ci-dessus, sur la page de choix du filtre , le bouton déconnexion est affiché ; sur la page suivante, c’est le bouton Retour.

Le problème, c’est que pour des raisons de simplicité et de maintenabilité, nous avons inséré la structure de la page jQM dans le layout principal, et non dans chaque vue  de l’application Rails. Heureusement, notre framework favori nous offre une solution élégante grâce à la fonction yield.

Vous avez forcément déjà utilisé yield pour passer au layout principal le code html de votre vue ; mais cette fonction permet de passer plusieurs élément de la vue vers un layout en nommant chaque yield. Si nous revenons sur le code de l’en-tête :

<div data-role="header" data-theme="b" >
  <%= yield :back %>
  <h1><%= yield :title %></h1>
  <%= yield :button %>
</div>
Nous allons pouvoir passer au layout trois éléments distincts :
  • Le bouton « back » ou autre élément sur la gauche de la barre d’en-tête
  • Le titre de la page
  • Un bouton « button » affiché à droite de la barre d’en-tête
Pour insérer notre titre et le bouton retour tel que vu sur la page d’affichage des catégories, il suffit d’insérer en haut de votre fichier de vue les codes suivants :
<% content_for :back do %><%= link_to "Retour", nil, :'data-rel'=>"back",:'data-icon'=>"arrow-l" %><% end %>
<% content_for :title do %><%= "Catégories" %><% end %>
La première ligne va afficher le bouton Back, avec une icône prédéfinie de  jQM (arrow left). La seconde va renvoyer un texte qui sera affiché comme titre de la fenêtre.
Dans le prochain article, nous parlerons des listes et de leur gestion avec Rails et JQuery Mobile.

Rails 3 + Paperclip sous Windows

Pour insérer et manipuler des images dans Rails, il existe de nombreux gems permettant de s’affranchir d’une bonne partie de la complexité des transferts de fichiers. Parmi ceux-ci, j’ai retenu Paperclip.

Autant la gestion des transferts de fichiers s’est faite sans problème, autant le redimensionnement de ceux-ci m’a amené à m’arracher quelques cheveux ; je tombais systématiquement sur l’erreur suivante :

Photo C:/xxx/xxx/xxx/Temp/stream,2568,0.jpg is not recognized by the 'identify' command

Pour régler ce problème, trois étapes importantes :

1. Installer ImageMagick
Eh oui, Paperclip repose sur ImageMagick pour le redimensionnement des images ! Il faut donc télécharger cet outil pour Windows à l’adresse suivante :

http://www.imagemagick.org/download/binaries/ImageMagick-6.6.4-9-Q16-windows-dll.exe

Installez le programme à l’adresse par défaut, puis vérifiez que tout fonctionne bien en ouvrant un terminal (invite de commande) et en tapant : identify chemin/vers/une/image.jpg. Si tout est OK, ImageMagick vous répondra en affichant le format du fichier que vous lui avez fourni.

2.  Ajouter un initialiseur à Rails

Ajoutez dans le répertoire config/initializers de votre application un fichier paperclip.rb dans lequel vous collerez le code suivant :

require "paperclip"
Paperclip.options[:command_path] = 'C:\PROGRA~1\ImageMagick-6.6.4-Q16'
Paperclip.options[:swallow_stderr] = false

Bien sûr, la première ligne d’option est à modifier en fonction de la localisation et de la version de ImageMagick installée à l’étape précédente. Il est très important de mettre le chemin Windows au format DOS 8.3 (PROGRA~1 en lieu et place de Program Files, dont l’espace cause problème à Paperclip).

3. Modifier les sources de Paperclip

Il faut également aller modifier la ligne de commande d’ImageMagick dans Paperclip, en éditant le fichier suivant paperclib.rb qui se trouve dans le répertoire lib du gem Paperclip. Sur mon poste, le fichier se trouve ici :

C:\Ruby\lib\ruby\gems\1.9.1\gems\paperclip-2.3.3\lib

Dans la fonction run, qui se trouve vers la ligne 100, trouvez la ligne suivante :

command = %Q[#{path_for_command(cmd)} #{params}]

et remplacez-la par la ligne ci-dessous :

command = %Q[#{path_for_command(cmd)} #{params.gsub("'",'"')}]

Cela remplacera les quotes simples par des quotes doubles.

Voilà, normalement, vous devriez accéder aux joies de Paperclip sous Windows !

Sources : Un commentaire du Railscast dédié à Paperpclip

Installer Passenger Phusion et Rails 2.2.2 sur Ubuntu 8.04

Logo PassengerCa fait déjà un moment que la promesse des développeurs de Phusion, à savoir un déploiement d’une application Rails en quelques lignes dans apache.conf me faisait saliver. J’avais, comme tout le monde, essayé de configurer un stack apache + mongrel, mais la complexité de la tâche m’avait fait reculer. Je me suis dit que l’arrivée de Rails 2.2.2, avec ses nombreuses nouveautés, était l’occasion rêvée de tester Passenger.

Lire la suite