Une Introduction à RubyCocoa, 1ère Partie
Cet article, ainsi que le prochain, font partie d’une série d’articles couvrant la programmation en Ruby sous Mac OS X.
Cependant, cet article peut-être utilisé sans référence aux précédents de la série.
Vous n’avez besoin que de quelques rudiments du langage Ruby avec un peu d’expérience en Xcode pour comprendre ce qui va suivre.
Cela étant dit, si vous avez envie d’en connaître un peu plus concernant Ruby, lisez les trois articles de la série GUI programming with Ruby/Tk. Et si vous n’avez que peu, voire pas d’expérience en Ruby, mon premier article est un tutoriel simple. Voilà, nous pouvons commencer.
Qu’est-ce que RubyCocoa ?
RubyCocoa est un framework qui offre un pont entre le langage de programmation Ruby et le framework Cocoa de Mac OS X.
Ce framework vous permet de créer des applications Mac OS X natives, basée sur Cocoa en utilisant Ruby.
Il a été créé par Hisakuni Fujimoto et est actuellement en version 0.4.0.
Il semble suffisamment stable pour développer des applications.
Dans cet article, je vais vous montrer comment utiliser ce framework en créant une simple petite interface pour la commande Unix tar.
Premièrement, je vous présenterai l’installation du framework RubyCocoa, puis nous verrons quelques éléments de base de son utilisation.
Enfin, je vous montrerai comment créer l’interface GUI de tar en utilisant Xcode, Ruby et le framework RubyCocoa.
Si tout se déroule correctement, une fois ce tutoriel terminé, vous aurez acquis les connaissances de base du développement d’applications Cocoa avec Ruby, et du fonctionnement d’une application créant et extrayant des fichiers tar.
Télécharger et installer RubyCocoa
J’ai écrit cet article, ainsi que le programme associé, sur un PowerBook G4 tournant sous Mac OS X 10.3.5. Mes instructions d’installation correspondent à cette environnement.
Si vous utilisez la version Jaguar de OS X, installez RubyCocoa à partir de l’image disque proposée ici.
Cependant, si vous utilisez Panther, comme moi, il vous sera peut-être nécessaire de télécharger la dernière version depuis le serveur CVS.
Pour cela, ouvrez un Terminal et tapez les commandes suivantes (sans le $).
$ cvs -d:pserver:anonymous@cvs.sf.net:/cvsroot/rubycocoa login
$ cvs -z3 -d:pserver:anonymous@cvs.sf.net:/cvsroot/rubycocoa co
-P -r branch-devel-panther -d rubycocoa-panther src
$ cd rubycocoa-panther
$ cvs update -d -P
Une fois que vous avez récupéré le framework RubyCocoa, vous pouvez l’installer en tapant les commandes suivantes dans ce même Terminal (assurez-vous d’être dans le même répertoire rubycocoa-panther qui a été créé au cours de la transaction CVS).
$ ruby install.rb config
$ ruby install.rb setup
$ sudo ruby install.rb install
La meilleure façon de vérifier que tout est bien installé est d’ouvrir Xcode et de sélectionner “New Project” dans le menu “Fichier”.
Vous devriez voir dans la boîte de dialogue “New Project” deux nouveaux éléments sous “Application” intitulés “Cocoa-Ruby Application” et “Cocoa-Ruby Document-based Application”.
Dans ce cas, votre installation est correcte.
Vous pouvez cliquer sur “Cancel” pour continuer ici.
Les bases de RubyCocoa
Le framework RubyCocoa crée un pont entre les classes Cocoa écrites en Objective-C et leur représentation Ruby.
Toutes les classes disponibles du framework Cocoa se trouvent dans le module RubyCocoa OSX, que l’on peut faire interagir avec nos propres classes pour les faire accéder à tout ce qui est proposé par le module.
En Objective-C, les méthodes ont une syntaxe étrange pour ceux qui utilisent un langage autre que Smalltalk.
Elles utilisent des espaces et des deux-points pour spécifier les différents paramètres qui seront passés à l’invocation de la méthode.
Ainsi, un appel de méthode en Objective-C ressemble à quelque chose comme :
[oPanel runModalForDirectory:self file:nil types:nil]
En RubyCocoa, on peut écrire en utilisant deux techniques différentes.
La première remplace les deux-points et espaces par des underscores, donnant aux méthodes un style Ruby :
oPanel.runModalForDirectory_file_types(self, nil, nil)
L’autre technique peut être utilisée pour donner un peu plus de sens au nom des méthodes qui sont extrêmement longs.
En utilisant cette technique, le nom des méthodes correspond au nom de la méthode Objective-C jusqu’au premier paramètre passé.
Le reste des arguments est passé dans la liste.
Ainsi, chaque argument est préfixé par un autre argument qui est un symbole Ruby dont le nom correspond au paramètre qu’il représente.
Je sais que cela semble un peu compliqué, mais l’exemple ci-dessous vous montre que c’est très simple une fois que l’on est habitué à la syntaxe.
oPanel.runModalForDirectory(self, :file, nil, :types, nil)
Une autre particularité que vous rencontrerez en utilisant RubyCocoa réside dans les valeurs retournées par les méthodes Cocoa.
Les méthodes Cocoa retournent les valeurs Cocoa, mais pas leur équivalent Ruby.
Ainsi, quand une méthode retourne une chaîne de caractères ou un tableau, c’est sous la forme NSString ou NSArray et pas son équivalent Ruby.
Les classes NSString et NSArray proposent les méthodes respectives to_s ou to_a.
Utilisez ces méthodes pour obtenir une chaîne de caractères ou un tableau Ruby.
Les méthodes de prédicat (qui retournent des valeurs booléennes) peuvent également vous perturber un peu.
Quand une méthode prédicat est appelée en RubyCocoa, la méthode retourne 0 ou 1, qui sont tous deux évalués à true en Ruby.
Pour éviter tout problème, vous devez suffixer le nom de la méthode par un point d’interrogation (?).
Ceci forcera la méthode à retourner un booléen Ruby.
oPanel.allowsMultipleSelection?
Enfin, un dernier problème que vous pourriez rencontrer en appelant les méthodes RubyCocoa survient lorsque vous utilisez une méthode dont le nom entre en conflit avec une méthode Ruby.
Quand cela arrive, il suffit de préfixer la méthode RubyCocoa par oc_.
Pour instancier des classes RubyCocoa, utilisez les mêmes méthodes que vous utilisez en Objective-C plutôt que la méthode new Ruby.
L’instanciation d’une classe NSObject ressemble au code suivant :
obj = NSObject.alloc.init
Bien que vous utilisiez les méthodes Cocoa pour créer les objets Cocoa, il n’est pas nécessaire d’utiliser les méthodes telles que release, autorelease et retain pour gérer la mémoire allouée à chaque objet puisque Ruby gère une garbage collecteur pour tous ces objets.
Vous pouvez en apprendre plus sur l’écriture d’applications RubyCocoa sur la page RubyCocoa programming page.
Bien. Maintenant que nous connaissons l’essentiel pour créer une application RubyCocoa, nous pouvons commencer la partie amusante de cette leçon.
La section suivante présentera la création de l’interface graphique de notre wrapper du programme tar.
Lancez Xcode (si ce n’était pas déjà fait) et lisez la suite.
La première étape dans l’écriture de notre interface pour le programme tar est de créer une application Cocoa-Ruby dans Xcode.
Nous repartons du point où nous avions commencé pour tester notre environnement RubyCocoa.
Sélectionnez “New Project” du menu “Fichier”de Xcode et choisissez “Cocoa-Ruby Application” dans la boîte de dialogue “New Project”.
Une fois que vous avez sélectionné le bon type d’application, donnez un nom à votre projet (j’ai appelé le mien “RubyCocoTar”) et sélectionnez son emplacement dans le fichier système et voilà !
Nous avons une nouvelle application RubyCocoa.
Elle ne fait pas grand chose pour le moment, mais nous allons y remédier rapidement.
Après avoir créé un nouveau projet RubyCocoa, nous devons créer notre interface en utilisant Interface Builder (IB).
Nous faisons exactement la même chose que ce que nous ferions pour une application Cocoa objective-C ou Java.
Double-cliquez sur le fichier “MainMenu.nib” dans le dossier “Resources” de Xcode.
Cette action ouvrira Interface Builder dans une nouvelle fenêtre que nous pourrons modifier.
La première chose à faire est de supprimer les menus inutiles et renommer la fenêtre principale et tous les éléments de menus restants.
Dans mon application, je n’ai gardé que le menu “NewApplication” (mais je l’ai renommé “RubyCocoaTar” pour correspondre au nom de mon application), et je me suis débarrassé de l’option “Preferences” dans ce menu.
Après avoir renommé les éléments de menu restants, vous pouvez poursuivre en créant l’interface.
Vous allez créer une application avec une interface à onglets, avec un onglet pour créer nos fichier tar, et un autre onglet pour extraire les fichier tar existants.
Cliquez sur la palette “Cocoa-Containers” et glissez un NSTabView sur la fenêtre principale.
Redimensionner l’instance de NSTabView et renommez les onglets “Create” et “Extract”, de telle façon qu’une fois terminée, l’application ressemble à l’image ci-dessous.

Figure 1. NSTabView ajouté à la fenêtre principale de RubyCocoaTar.
Une fois que l’objet NSTabView est positionné correctement, ajoutez le reste des éléments à chacun des deux onglets.
Dabord, nous ajouterons les composants nécessaires à l’onglet “Create”.
Nous essaierons de garder notre interface principale aussi simple que possible.
Pour la section “Create tar file” de notre interface, nous avons seulement besoin de l’objet NSTabView pour afficher les fichiers que nous avons choisis de tarer et compresser, ainsi que trois boutons pour ajouter, supprimer et tarer les fichiers.
Poursuivez en ajoutant ces éléments à l’onglet “Create” de telle sorte qu’il ressemble à l’image ci-dessous.
Assurez-vous que NSTableView ne comporte qu’une colonne, et renommez celle-ci en “Files”.

Figure 2. L’onglet “Create” de l’application RubyCocoa.
Je préfère terminer complètement mon interface avant de créer le contrôleur de classe et d’ajouter les connexions.
Nous allons donc continuer en ajoutant les éléments de l’interface dans l’onglet “Extract”, puis nous reviendrons pour créer la classe contrôleur et toutes ses connexions.
Voyant la simplicité de l’onglet “Create”, il est à peine croyable de penser que l’onglet “Extract” pourrait être plus simple, mais vous avez deviné - c’est le cas.
L’onglet “Extract” ne nécessite qu’un NSTextField pour contenir le nom du fichier que nous souhaitons décompresser et détarer, un bouton pour appeler NSOpenPanel pour retrouver le fichier, et un bouton pour appeler notre méthode d’extraction, plus un label et un séparateur pour enjoliver l’interface.
Après avoir ajouté ces éléments à notre interface, l’onglet “Extract” devrait ressembler à l’image ci-dessous.

Figure 3. L’onglet “Extract” de l’application RubyCocoaTar.
Bien, nous avons ajouté tous les éléments majeurs de notre interface et nous devons simplement créer la classe contrôleur et ajouter quelques connexions.
Cependant, nous devons ajouter un dernier et mineur élément d’interface.
Il y a trois types de compression que nous allons proposer dans notre application et pour cela, en plus du tar sans compression, nous aurons quatre types de fichiers différents : .tgz, .bz2, .Z, .tar.
Le panneau NSSavePanel doit également permettre à l’utilisateur de choisir le type de fichier tar qu’il souhaite créer (c.à.d : .tgz, .bz2, .Z ou .tar).
Pour cela, nous devons créer un nouvel objet NSView qui contiendra un NSPopUpButton affichant la liste déroulante des extensions de fichiers utilisables avec NSSavePanel.
Poursuivons et créons notre objet NSView, et un peu plus tard nous verrons comment l’ajouter à NSSavePanel.
Pour créer la vue de nos types de fichiers, nous avons besoin d’ajouter une instance de la classe NSView à notre fichier nib.
Cliquez sur la palette “Cocoa-Containers” et glissez l’élément NSView sur l’onglet Instances de la fenêtre “MainMenu.nib”.
Ceci devrait faire apparaître une nouvelle fenêtre ayant pour titre “View” dans laquelle nous ajouterons notre liste de ce type de fichiers.
Si ce n’est pas le cas, double-cliquez sur l’instance de NSView que vous avez glissée sur la fenêtre “MainMenu.nib” et la fenêtre “View” devrait apparaître.
Vous pouvez changer le nom de la vue en ce qui vous plaira, bien que cela ne soit pas absolument nécessaire, puisque nous ne sommes pas vraiment aussi intéressés par les objets NSView que nous le sommes par la liste déroulante de types de fichiers.
Une fois que nous avons une fenêtre affichant notre vue personnalisée, nous pouvons y ajouter le label et la liste déroulante (NSTextField et NSPopUputton).
Dans notre application, nous allons donner à l’utilisateur l’option de tarer un groupe de fichier avec ou sans compression.
Nous avons quatre possibilités d’extension pour tarer le fichier : .tgz, .bz2, .Z, .tar.
Comme le NSPopUpButton ne nous propose que trois éléments de menu par défaut, nous devons ajouter un élément au composant.
Pour cela, glisser un “Menu Item” depuis la palette “Cocoa-Menus” sur le nouveau NSPopUpButton de notre vue.
Une fois que nous avons quatre éléments, nous pouvons continuer et changer chacun selon les quatre extensions de fichiers mentionnées ci-dessus.
Une fois terminée, votre vue devrait ressembler à l’image ci-dessous :

Figure 4. Vue permettant la sélection du type de fichier.
Voilà qui est fait concernant le design de l’interface de notre application.
Maintenant que notre GUI est achevée, nous pouvons continuer en créant la classe contrôleur pour notre interface et connecter les actions et les outlets.
La partie suivante traite de cette étape.
Créer la classe contrôleur
Tout comme pour la classe contrôleur Objective-C d’une application Cocoa, notre classe Controller doit hériter de la classe NSObject.
Donc, notre première étape est de sous-classer la classe NSObject.
Trouver la classe NSObject dans l’onglet “Classes” de la fenêtre “MainMenu.nib”.
Sélectionnez cette classe dans la liste et appuyez sur le bouton “Return”.
Ceci devrait créer une nouvelle sous-classe de l’objet NSObject appelé MyObject.
Sélectionnez la nouvelle classe fille de NSObject, si elle ne l’est pas déjà, et modifiez son nom de MyObject en Controller.
Une fois que nous avons créé et renommé notre nouvelle classe Controller, nous avons besoin d’y ajouter les actions nécessaires et les outlets.
Assurez vous que la classe Controller est sélectionnée et ouvrez la fenêtre Info (vous pouvez le faire en sélectionnant l’élément “Show Info” sous le menu “Tools”, ou en utilisant la combinaison de touche “Shift-Command-I”) et sélectionnez “Attributes” dans la liste déroulante.
Tout dabord, nous allons ajouter les outlets.
Cliquez sur l’onglet “Outlets” et ajoutez cinq nouveaux outlets à la liste : 1) fileTableView, 2) archiveFile, 3) fileType, 4) fileTypeView et 5) mainWindow.
Après avoir ajouté tous les outlets à notre classe Controller, nous devons ajouter les actions qui effectueront le travail.
Pour cela, cliquez sur l’onglet Actions et ajoutez les actions suivantes à la liste : 1) addFile, 2) removeFile, 3) createArchive, 4) extractArchive et 5) browseForArchive.
Nous avons maintenant une classe Controller suffisamment implémentée pour nous permettre de créer toutes nos connnexions entre le fichier nib que nous avons créé et notre application Ruby.
Avant de commencer, cependant, nous devons créer une instance de notre classe Controller.
Pour cela, sélectionnez la classe Controller depuis la liste sous l’onglet “Classes” dans la fenêtre “MainMenu.nib”.
Ensuite, vous pouvez soit faire un control-clic et sélectionner “Instanciate Controller” ou vous pouvez le sélectionner dans le menu “Classes&lrquo; dans la bar de menu d’Interface Builder.
Les trois premiers outlets sont utilisés pour mettre à jour et accéder aux informations que l’utilisateur nous donne via l’interface.
Les deux derniers sont nécessaire seulement pour utiliser les classes NSOpenPanel et NSSavePanel.
Vous devrez les connecter aux outlets de l’interface qui correspondent.
Ceci s’effectue en effectuant un control-clic sur l’instance nouvellement créée de notre classe Controller et en étirant la ligne qui apparaît jusqu’à l’élément de l’interface qu’il représente.
L’outlet fileTableView devrait être connectée à l’objet NSTableView dans l’onglet “Create” qui afficher les fichiers que nous avons sélectionnés pour être archivés.
L’outlet archiveFile devrait pointer sur le NSTextField (le champ texte, pas le label) dans l’onglet “Extract” qui contiendra le nom du fichier archive que nous avons choisi d’extraire.
fileType doit être connecté au NSPopUpButton qui est placé dans notre vue personnalisée pour nous permettre de choisir le type de fichier choisi par l’utilisateur pour la création de la nouvelle archive.
L’outlet fileTypeView est un pointeur vers la vue personnalisée que nous avons créée pour le NSSavePanel, et enfin notre outlet mainWindow est un pointeur vers la fenêtre principale de notre application.
Vous avez maintenant créé la moitié des connexions de votre classe Controller.
Ensuite, nous aurons besoin d’attacher chacune de ces actions à un élément de l’interface.
Voyons ce que chacun est censé faire et à quel élément il sera attaché.
Connecter des actions aux éléments de l’interface qui les déclenchent se fait de façon un peu différente que la connexion des outlets à leur éléments d’interface.
Plutôt que de faire ctrl-clic sur l’objet Controller et glisser la ligne de l’élément d’interface, nous allons revenir en arrière, et effectuer un ctrl-clic sur l’élément d’interface qui déclenche l’action et glisser une ligne jusqu’à l’objet Controller.
Commençons par les actions des boutons ‘+’ et ‘-’.
L’action addFile appelle une instance de NSOpenPanel et permet à l’utilisateur de sélectionner un ou plusieurs fichiers qu’il souhaite inclure dans leur archive.
L’action removeFile supprime simplement le fichier réellement sélectionné dans la liste.
Nous devons connecter des deux actions aux boutons ‘+’ et ‘-’ sous l’objet NSTableView dans l’onglet “Create”.
Les actions createArchive et extractArchive implémentent la création des nouveaux fichiers d’archives et l’extraction du contenu d’un fichier d’archive existant vers un répertoire donné.
La première action affiche un NSSavePanel permettant à l’utilisateur de sélectionner un emplacement - et donner un nom- pour l’archive en cours de création.
Nous devrions créer une connexion entre cette action et le bouton “Create Archive” dans l’onglet “Create” en utilisant la même méthode que nous avons déjà utilisée pour nos actions ajouter et supprimer un fichier.
L’action extractArchive exécute le programme tar pour extraire le fichier archive sélectionné vers le répertoire choisi par l’utilisateur en lui proposant une classe extractArchive.
Enfin, l’action browseForArchive permet à l’utilisateur de sélectionner un fichier archive à extraire en utilisant un objet NSOpenPanel.
Elle est appelée chaque fois qu’un utilisateur clique sur le bouton “Browse” dans l’onglet “Extract”, et a donc besoin d’être connectée au bouton en suivant le même procédé que celui décrit auparavant.
Ceci résume tout ce que nous avons besoin de faire au moyen d’Interface Builder.
Le reste de notre travail devra se faire par Xcode.
Normalement, à ce point, nous devrions sélectionner notre classe Controller et créer les fichiers avec Interface Builder qui contiendront le squelette du code que nous implémenterons avec Xcode.
Cependant, il ne le fera qu’en Objective-C et Java, pas en Ruby.
Nous aurons un peu de travail à faire avant.
Assurez-vous d’avoir sauvegardé votre fichier nib avant de fermer Interface Builder et Xcode.
Le prochain article vous indiquera comment créer le squelette du code qu’Interface Builder crée habituellement pour nous, et nous terminerons notre projet en ajoutant le reste du code Ruby dont nous avons besoin pour que l’application fonctionne.
En attendant, j’espère que ce tutoriel vous occupera !

Textes originaux en anglais sur O’Reilly : An Introduction to RubyCocoa, Part 1 par Christopher Roach
Chargement
Commentaires récents