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.
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.
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 :
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)
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.