Plusieurs postes sont ouverts, consultez nos besoins et déposez nous une candidature.
Nicolas Cavigneaux
29 03 2011

MacRuby - Introduction à HotCocoa - Ajout de fonctionnalités

posté par dans les catégories technologies, alternatives, interpréteur, apple

Dans nos précédents articles concernant HotCocoa nous avons vu les bases de la création d’un projet ainsi que la mise en place de l’interface utilisateur. Pour l’heure, les interactions utilisateurs sont gérées de manière factices. Nous allons aujourd’hui mettre en place les routines réelles de récupération et d’affichage des flux RSS / ATOM.

Préparation

Le but est de récupérer un flux RSS (ou ATOM) depuis l’adresse fournie par l’utilisateur. Plutôt que d’analyser nous même le flux récupéré nous allons utiliser un gem pour nous faciliter la tâche. En effet, il est tout à fait possible d’utiliser des gems écrits pour Ruby MRI depuis MacRuby. Les deux interpréteurs étant compatibles, vous ne devriez pas rencontrer de problèmes hormis pour certains gems écrits en C et non en Ruby pur.

J’ai choisi d’utiliser FeedParser qui suffira largement à notre utilisation. Avant de pouvoir l’utiliser il faut bien évidemment l’installer, nous allons donc passer par macgem (version dédiée à la gestion des gems pour MacRuby) :

macgem install ruby-feedparser
                    

Nous pouvons maintenant passer à l’écriture du code. Vous retrouverez le code complet sur notre compte GitHub.

require 'rubygems'
                    require 'hotcocoa'
                    require 'feedparser'
                    require 'open-uri'
                    
                    class Application
                    
                      include HotCocoa
                    
                      def start
                        application :name => "SynbiozFeeds" do |app|
                          app.delegate = self
                          window(:size => [640, 480], :center => true, :title => "Synbioz Feed", :view => :nolayout) do |win|
                            win.will_close { exit }
                    
                            win.view = layout_view(:layout => {:expand => [:width, :height], :padding => 0, :margin => 0}) do |vert|
                              vert << layout_view(:frame => [0, 0, 0, 40], :mode => :horizontal, :layout => {:padding => 0, :margin => 0, :start => false, :expand => [:width]}) do |horiz|
                                horiz << label(:text => "Flux RSS", :layout => {:align => :center})
                                horiz << @feed_field = text_field(:layout => {:expand => [:width]})
                                horiz << button(:title => 'lire', :layout => {:align => :center}) do |b|
                                  b.on_action { load_feed }
                                end
                              end
                    
                              vert << scroll_view(:layout => {:expand => [:width, :height]}) do |scroll|
                                scroll.setAutohidesScrollers(true)
                                scroll << @table = table_view(:columns => [column(:id => :data, :title => '')], :data => []) do |table|
                                   table.setUsesAlternatingRowBackgroundColors(true)
                                   table.setGridStyleMask(NSTableViewSolidHorizontalGridLineMask)
                                   table.on_action do
                                     url = NSURL.URLWithString(table.dataSource.data[table.clickedRow][:link])
                                     NSWorkspace.sharedWorkspace.openURL(url)
                                   end
                                end
                              end
                            end
                          end
                        end
                      end
                    
                      def load_feed
                        url = @feed_field.stringValue
                        unless url.nil? || url =~ /^\s*$/
                          feed = FeedParser::Feed.new open(url).read
                          latest_posts = feed.items[0..9]
                    
                          latest_posts.each do |p|
                            @table.dataSource.data << {:data => "#{p.title} par #{p.creators.join(', ')}", :link => p.link}
                          end
                          @table.reloadData
                        end
                      end
                    end
                    
                    Application.new.start
                    

Détaillons les quelques changements qui rendent notre application fonctionnelle.

Chargement des données d’un flux

Interface vide

L’idée étant de pouvoir entrer l’URL d’un flux RSS ou ATOM pour en récupérer les 10 dernières entrées et les afficher dans un tableau par ordre chronologique inverse. Il faut donc pouvoir lire les données distante (flux XML) et les interpréter.

Voici comment procéder :

def load_feed
                      url = @feed_field.stringValue
                      unless url.nil? || url =~ /^\s*$/
                        feed = FeedParser::Feed.new open(url).read
                        latest_posts = feed.items[0..9]
                    
                        latest_posts.each do |p|
                          @table.dataSource.data << {:data => "#{p.title} par #{p.creators.join(', ')}", :link => p.link}
                        end
                        @table.reloadData
                      end
                    end
                    

Il faut commencer par modifier notre méthode load_feed.

On récupère l’URL du flux entré par l’utilisateur qu’on lit directement grâce à open. open fournit par open-uri permet, entre autre, de lire directement le contenu d’une réponse HTTP. On obtient donc une chaîne contenant notre flux RSS / ATOM.

Maintenant entre en jeu FeedParser qui va nous retourner une structure de données contenant les articles ainsi que toutes les infos dont on peut avoir besoin.

À la ligne suivante, on ne conserve que les 10 derniers articles parus. On parcourt ensuite ces articles pour mettre à jour le tableau qui sera affiché à l’utilisateur. Pour chaque article, on crée une ligne dans laquelle on affiche le titre et l’auteur.

Vous noterez qu’en plus des données (clé :data), on passe également le lien (clé :link) vers la page web de l’article. Ceci sera utilisé pour ouvrir le navigateur de l’utilisateur lorsqu’il cliquera sur un titre d’article.

Voici le résultat obtenu :

Interface avec articles

Interaction utilisateur

Modifions maintenant le code qui génère notre tableau :

vert << scroll_view(:layout => {:expand => [:width, :height]}) do |scroll|
                      scroll.setAutohidesScrollers(true)
                      scroll << @table = table_view(:columns => [column(:id => :data, :title => '')], :data => []) do |table|
                         table.setUsesAlternatingRowBackgroundColors(true)
                         table.setGridStyleMask(NSTableViewSolidHorizontalGridLineMask)
                         table.on_action do
                           url = NSURL.URLWithString(table.dataSource.data[table.clickedRow][:link])
                           NSWorkspace.sharedWorkspace.openURL(url)
                         end
                      end
                    end
                    

Seul un bloc d’instructions a été ajouté. Il permet, lorsque l’utilisateur clique sur une ligne de résultat, de charger l’article dans le navigateur par défaut.

On définie donc le comportement de l’action via la méthode on_action qui prend un bloc en paramètre. Dans ce bloc, on récupère depuis la source de données l’adresse associée à la ligne cliquée : table.dataSource.data[table.clickedRow][:link]. On utilise ensuite NSURL.URLWithString() sur la chaîne récupérée pour obtenir une URL facilement manipulable par les classes et méthodes Cocoa.

Il ne nous reste plus maintenant qu’à demander l’ouverture de l’URL dans le navigateur par défaut. Cocoa nous propose une classe et des méthodes pour le faire en toute simplicité : NSWorkspace.sharedWorkspace.openURL(url)

Conclusion

Avec seulement quelques lignes de code modifiées, on passe d’une maquette sans vie à quelque chose de fonctionnel.

On voit clairement que grâce à la concision de Ruby et la richesse de Cocoa, il est très simple de prototyper une application pour ensuite lui ajouter des fonctionnalités. Je trouve réellement plaisant de pouvoir écrire des applications Mac natives si facilement et de surcroît à l’aide de mon langage préféré sans pour autant sacrifier les performances.

L’équipe Synbioz.

Libres d’être ensemble.

Articles connexes

09 11 2010

Introduction à MacRuby

MacRuby, qu’est ce que c’est ? MacRuby est une implémentation alternative de Ruby 1.9 basée sur le coeur des technologies Mac OS X telles que les librairies d’Objective-C, son garbage collector ou e…

Lire la suite
23 05 2013

Introduction à RubyMotion

RubyMotion est un framework permettant d’écrire des applications iOS et OS X natives en Ruby. Le code Ruby est compilé exactement de la même façon que le serait du code Objective C. Toutes les APIs C…

Lire la suite

Ajouter un commentaire

Notre expérience vous intéresse ? Inscrivez-vous à nos articles !

×

Newsletter

Rejoignez-nous !

Poursuivons la conversation

N° Vert
0 805 69 35 35

Nos dernières nouvelles

Nos derniers tweets

Allo maman j'ai encore cassé l'API. Découvrez les nouveautés de l'API Facebook http://t.co/1QThuKx2NN

@AngeZanetti joker !

Followers not found http://t.co/vAhGfpPPpX