Résumé, par DHH, des nouvelles fonctionnalités qui seront présentes dans Rails 2.0.
DHH vient d’annoncer la sortie officielle d’une preview release pour Rails 2.0. Elle sera la première d’une courte série d’itérations (release candidates) qui devraient rapidement converger vers la version finale de Rails 2.0. A noter que la version 1.2.4 devrait entre temps également voir le jour: elle contiendra un ensemble de corrections de bugs et les derniers “deprecation warnings” destinés à faciliter la migration d’une application existante vers la version 2.0.
Voici une liste des quelques nouveautés à découvrir dans cette nouvelle mouture de notre framework favori. Note: ce post est une traduction plus ou moins fidèle de l’article original de DHH.
Action Pack: Ressources
L’essentiel du travail sur Rails 2.0 concerne les ressources; de nombreuses améliorations ont été ajoutées au style RESTful. Tout d’abord, le point-virgule dans les URLs des ressources, destiné à précéder le nom d’une action personnalisée, est remplacé par un slash: /people/1;edit devient maintenant /people/1/edit.
De plus, il est maintenant possible d’utiliser des espaces de noms dans le routage des ressources, ce qui facilite grandement le cloisonnement d’une partie du site, comme par exemple pour une interface d’admin:
map.namespace(:admin) do |admin|
admin.resources :products,
:collection => { :inventory => :get },
:member => { :duplicate => :post },
:has_many => [ :tags, :images, :variants ]
end
Cela permet d’avoir des routes nommées telles que inventory_admin_products_url et admin_product_tags_url. Pour permettre de s’y retrouver devant la prolifération de routes, une tâche “rake route” permet de lister toutes les routes nommées créées par routes.rb.
Une nouvelle convention impose aux contrôleurs de ressources d’être au pluriel par défaut. Cela permet à une resource d’être mappée dans différents contextes tout en continuant à référencer le même contrôleur:
# /avatars/45 => AvatarsController#show
map.resources :avatars
# /people/5/avatar => AvatarsController#show
map.resources :people, :has_one => :avatar
Action Pack: Multivues
Toujours dans l’esprit RESTful, des améliorations ont été apportées aux multivues. On avait déjà ”#respond_to”, qui a été amélioré pour fouiller dans les templates. Ainsi, le format d’un template devient indépendant de son moteur de rendu: “show.rhtml” devient “show.html.erb”, qui sera le template rendu par défaut pour une action “show” qui a déclaré “format.html” dans son respond_to. Il est donc maintenant possible d’avoir des vues telles que “show.csv.erb”, qui répondra au type MIME “text/csv”, mais toujours avec le moteur de rendu ERB.
Le nouveau format des templates est donc action.format.moteur_de_rendu. Quelques exemples:
- show.erb: utilise le même template “show” pour tous les formats
- index.atom.builder: utilise le moteur Builder (autrefois identifié par “rxml”), pour générer une action index pour le type MIME “application/atom+xml”
- edit.iphone.haml: utilise le moteur de rendu HAML (non-inclus par défaut dans Rails) pour générer une action “edit” pour le format particulier “Mime::IPHONE”
En parlant d’iPhone, il est maintenant possible de déclarer des “faux” types, utilisés uniquement pour le routage interne. Par exemple si vous désirez une interface HTML spéciale pour iPhone. Tout ce dont vous avez alors besoin est ce qui suit:
# à placer dans config/initializers/mime_types.rb
Mime.register_alias "text/html", :iphone
class ApplicationController < ActionController::Base
before_filter :adjust_format_for_iphone
private
def adjust_format_for_iphone
if request.env["HTTP_USER_AGENT"] &&
request.env["HTTP_USER_AGENT"][/(iPhone|iPod)/]
request.format = :iphone
end
end
end
class PostsController < ApplicationController
def index
respond_to do |format|
format.html # génère index.html.erb
format.iphone # génère index.iphone.erb
end
end
end
Il est encouragé de déclarer ses propres alias MIME dans le fichier config/initializers/mime_types.rb. Ce fichier est inclus par défaut dans toutes les nouvelles applis.
Action Pack: Identification des enregistrements
Sur base de ces améliorations RESTful, un certain nombre de simplifications sont désormais possibles pour les méthodes des vues et des contrôleurs qui manipulent des URLs. Un ensemble de conventions ont été ajoutées pour transformer au vol des classes modèles en routes de ressources:
# person est objet Person, qui par convention sera mappé
# en person_url pour retrouver l'URL de la ressource correspondante
redirect_to(person)
link_to(person.name, person)
form_for(person)
Action Pack: L’amour du HTTP
Comme vous l’aurez compris, la version Rails 2.0 d’Action Pack est résolument dédiée à un rapprochement vers HTTP dans toute sa gloire. Des ressources, des représentations (vues) multiples, mais plus encore. Un nouveau module permet maintenant d’utiliser l’authentification HTTP Basic, qui s’avère être une excellente manière de faire de l’authentification sur API via SSL. C’est bigrement simple à utiliser; voici un exemple (il y en a d’autres dans ActionController::HttpAuthentication) :
class PostsController < ApplicationController
USER_NAME, PASSWORD = "dhh", "secret"
before_filter :authenticate, :except => [ :index ]
def index
render :text => "Everyone can see me!"
end
def edit
render :text => "I'm only accessible if you know the password"
end
private
def authenticate
authenticate_or_request_with_http_basic do |user_name, password|
user_name == USER_NAME && password == PASSWORD
end
end
end
Il est désormais également plus simple de structurer ses fichiers Javascript et CSS en unités logiques, sans être embêté par le surcoût HTTP causé par le fait de requérir une foultitude de fichiers. En utilisant javascript_include_tag(:all, :cache => true), Rails 2.0 transformera en production l’ensemble des fichiers public/javascripts/.js en un public/javascripts/all.js unique, tout en conservant les fichiers séparés en développement; cela permet de travailler de manière itérative sans devoir nettoyer la cache à chaque fois.
Dans le même ordre d’idées, une nouvelle option permet de tromper les navigateurs qui n’aiment pas sérialiser eux-mêmes les requêtes vers une même machine. En spécifiant ActionController::Base.asset_host = "assets%d.example.com", Rails distribuera automatiquement les reférences à des fichiers statiques (comme image_tag) vers vos différents serveurs de contenus (assets1.example.com, assets2.example.com, et ainsi de suite). Cela permet au navigateur d’ouvrir beaucoup plus de connexions simultanées, et donc d’améliorer la vitesse apparente de votre application.
Action Pack: Sécurité
Rails 2.0 rend encore plus aisée la création d’applications automatiquement sécurisées.
Il y a désormais un mécanisme inclus pour gérer les attaques CRSF. En incluant un token spécial dans tous vos formulaires et requêtes AJAX, il vous est possible d’empêcher que des requêtes soient faites de l’extérieur de votre application. Tout ceci est activé par défaut dans les nouvelles applications Rails 2.0, et il est très simple de l’activer dans les applis existantes en utilisant ActionController::Base.protect_from_forgery (voir ActionController::RequestForgeryProtection pour plus d’infos).
Il est également plus simple de gérer les attaques XSS tout en permettant malgré tout aux utilisateurs d’insérer de l’HTML dans une page. La vieille méthode TextHelper#sanitize est passée d’une approche black-list (très difficile à garder sécurisée) à une approche white-list. Si vous utilisez déjà sanitize, l’amélioration sera immédiatement appliquée. Il est également possible de définir soi-même les tags permis par défaut (voir TextHelper#sanitize pour les détails).
Enfin, le support pour les cookies HTTP-only a été ajouté. Ils ne sont pas encore supportés par tous les navigateurs, mais on peut les utiliser là où c’est possible.
Action Pack: Gestion des exceptions
Il est généralement plus intéressant de gérer des exceptions fréquentes à un niveau partagé plutôt que par action. Cela a toujours été possible en surdéfinissant rescue_action_in_public, mais il fallait alors faire soi-même une instruction case et appeler super. Désormais, Rails propose au niveau de la classe une macro appelée “rescue_from”, que l’on peut utiliser pour spécifier de manière déclarative certaines exceptions à une action donnée:
class PostsController < ApplicationController
rescue_from User::NotAuthorized, :with => :deny_access
protected
def deny_access
...
end
end
Action Pack: Divers
A noter également l’apparition de AtomFeedHelper, qui rend encore plus simple la création de flux Atom en utiliser une syntaxe Builder améliorée:
# index.atom.builder:
atom_feed do |feed|
feed.title("Mon super blog!")
feed.updated((@posts.first.created_at))
for post in @posts
feed.entry(post) do |entry|
entry.title(post.title)
entry.content(post.body, :type => 'html')
entry.author do |author|
author.name("DHH")
end
end
end
end
Un certain nombre d’améliorations de performance on été faites, donc les appels de contenu statique sont beaucoup plus rapides, et les routes nommées simples sont cachées, ce qui les rend également plus rapides.
Enfin, in_place_editor et autocomplete_for sont désormais des plugins; il ne font plus partie intégrante du noyau Rails.
Active Record: Performance
De nombreuses petites améliorations et corrections ont été apportées à ActiveRecord mais il n’y a que peu de nouvelles fonctionnalités importantes. Une de celles-ci est un simple cache de requête qui sera capable de reconnaître les appels SQL similaires dans la même requête et de retourner le résultat placé en cache. Ce cache pourrait être particulièrement utile pour les nombreuses situations qui peuvent être difficiles à gérer avec :include ou d’autres mécanismes. Les performances des fixtures ont été nettement améliorées, ce qui rend la plupart des suites de test basées sur des fixtures 50 à 100% plus rapide.
Active Record: Sexy migrations
Il existe une nouvelle alternative pour déclarer les migrations dans un format légèrement plus efficace. Avant, vous devier écrire:
create_table :people do |t|
t.column, "account_id", :integer
t.column, "first_name", :string, :null => false
t.column, "last_name", :string, :null => false
t.column, "description", :text
t.column, "created_at", :datetime
t.column, "updated_at", :datetime
end
Maintenant, vous pouvez écrire:
create_table :people do |t|
t.integer :account_id
t.string :first_name, :last_name, :null => false
t.text :description
t.timestamps
end
Active Record: récupérer du XML, emballer du JSON
Active Record supporte la sérialisation vers XML depuis un bout de temps. Dans Rails 2.0, la désérialisation a également été ajoutée. Il est désormais possible d’écrire Person.new.from_xml("David") et récupérer ce que vous attendez. La sérialisation a aussi été ajoutée à JSON. Celui-ci supporte la même syntaxe que la sérialisation XML (y compris les associations imbriquées). Il suffit de faire person.to_json.
Active Record: Un sérieux régime
Pour rendre Active Record plus léger, les fonctionnalités acts_as_xyz on été supprimées et mises dans des plugins individuels sur le dépôt SVN de Rails. Donc, imaginons que vous ayez besoin d’utiliser acts_as_list, la seule chose que vous devez faire est ./script/plugin install acts_as_list et tout reviendra comme s’il ne s’était jamais rien passé.
De manière un peu plus drastique, les adaptateurs pour les bases de données commerciales ont été placés dans leurs propres gems. Maintenant, Rails est fourni uniquement avec les adaptateurs pour MySQL, SQLite et PostgreSQL. Ce sont les bases de données auquel l’accès est aisé et avec lesquelles les tests devraient être réalisés. Mais cela ne signifie pas que les bases de données commerciales sont laissées sur le côté. Il faut plutôt comprendre qu’elles sont libre d’évoluer indépendamment de la distribution principale de Rails. C’est probablement une bonne chose car les bases de données commerciales tendent à nécessiter beaucoup plus d’exceptions et de changements pour fonctionner correctement.
Les adaptateurs pour les bases de données commerciales sont maintenant dans des gems qui suivent la convention de nommage suivante activerecord-XYZ-adapter. Donc, si vous tapez gem install activerecord_oracle_adapter, Oracle sera instantanément disponible comme choix d’adaptateur dans toutes les application Rails présentes sur la machine. Vous ne devrez pas changer une seule ligne dans vos applications pour commencer à l’utiliser.
Cela signifie également qu’il sera plus facile pour les nouveaux adaptateurs de base de données d’entrer dans le monde de Rails. Pourvu que vous publiiez votre adaptateur en suivant les conventions, les utilisateurs devront uniquement installer la gem correspondante et seront prêt à utiliser votre DB.
Active Record: with_scope avec un trait de vinaigre syntaxique
ActiveRecord::Base.with_scope est devenu protected pour décourager les programmeurs de l’utiliser à mauvais escient dans les contrôleurs (spécialement dans les filtres). A la place, il est conseillé de l’utiliser dans le modèle lui-même. C’est ce pour quoi cette méthode a été prévue et c’est l’endroit où elle doit logiquement rester. Mais, bien entendu, il s’agit seulement d’encourager un usage et d’en décourager un autre. Si vous avez pesé les pour et les contre et que vous voulez toujours utiliser with_scope en dehors du modèle, vous pouvez toujours le faire grâce à object.send(:with_scope).
ActionWebService out, ActiveResource in
Ce n’est pas une surprise si Rails a choisi son camp dans le débat SOAP vs REST. A moins que vous ne deviez absolument utiliser SOAP pour des questions d’intégration, il est vivement découragé de le faire. Comme extension naturelle de cette position, ActionWebService a été retiré du paquet par défaut de Rails. Il y a bien un gem install actionwebservice mais, néanmoins, le message est clair.
En même temps, le nouveau framework ActiveResource est sorti de sa version bêta et est maintenant inclus dans le paquet par défaut. ActiveResource est comme ActiveRecord, mais pour les ressources. Il suit une API similaire et est configuré pour les applications Rails utilisant une approche orientée-resource. Par exemple, un vanilla scaffold sera rendu accessible par ActiveResource.
ActiveSupport
Il n’y a pas tellement de neuf dans ActiveSupport. Il y a quelques nouvelles méthodes telles que Array#rand pour obtenir un élément aléatoire d’un tableau, Hash#except pour filtrer un hachage des clefs non désirées et beaucoup d’extensions pour Date. Le testing a été un peu simplifié grâce à assert_difference. A part ça, il y a juste des petites corrections et améliorations.
Action Mailer
Il y a une modeste mise à jour pour ActionMailer. Outre une série de bugs corrigés, une option pour enregistrer un moteur de modèle alternatif a été ajoutée ainsi que assert_email dans la suite de test. Ce dernier fonctionne comme suit:
# Assert number of emails delivered within a block:
assert_emails 1 do post :signup, :name => ‘Jonathan’ end
Rails: Le debugger est de retour
Pour consolider le tout, une série d’améliorations ont été apportées à Rails en général. Parmi celles-ci, le retour du breakpoint sous forme de debugger. Cette fois-ci Rails bénéficie d’un réel debugger, pas juste un saut vers IRB. Il est possible d’avancer et de reculer dans le code, lister la position courante et bien plus. Tout cela provient de la gem ruby-debug. Vous devrez donc l’installer pour que le nouveau debugger puisse fonctionner.
Pour utiliser le debugger, vous devez juste installer la gem, mettre debugger quelque part dans votre application, et ensuit démarrer le serveur avec --debugger ou -u. Lorsque le code exécute la commande debugger, vous pourrez accéder au programme directement à partir du terminal où est lancé le serveur. Pas besoin de script/breakpointer ou quoi que ce soit d’autre. Vous pouvez aussi utiliser le debugger dans vos tests.
Rails: Nettoyer votre environnement
Avant Rails 2.0, les fichiers config/environment.rb étaient obstrués avec toutes sortes de détails de configuration. Maintenant, il est possible de rassembler tous ces éléments dans des fichiers distincts, de les mettre dans config/initializers et ils seront automatiquement chargés. Les nouvelles applications Rails 2.0 sont créées avec deux exemples: inflexions.rb (pour les règles de pluralisation) et mimi_types.rb (pour les mime types). Ceci devrait vous assurer de ne rien garder dans config/environment.rb à part ce qui y est placé par défaut.
Rails: Spécification simplifiée de l’ordre de chargement des plugins
Maintenant que pas mal de fonctionnalités ont été enlevée de Rails pour être placée dans des plugins, il est probable qu’il existe des dépendances entre ces plugins. Vous pourriez, par exemple, devoir charger acts_as_list avant acts_as_very_cool_list pour que le second puisse étendre le premier.
Avant, cela exigeait que vous nommiez tous les plugins dans config.plugins. C’était beaucoup de travail lorsque vous vouliez dire “Je veux seulement que acts_as_list soit chargé avant tout le reste”. Maintenant, vous pouvez exactement faire cela avec config.plugins=[:acts_as_list, :all] .
Et des centaines de centaines d’autres améliorations
Ce dont nous avons parlé ci-dessus est une minuscule partie du paquet 2.0 complet. Il y a littéralement des centaines de bugs corrigés, d’améliorations, de fonctionnalités étendues inclues dans Rails 2.0. Tout ceci provient du travail de tonnes de contributeurs dévoués travaillant sans relâche pour améliorer le framework.
David vous encourage à plonger dans le CHANGELOG et apprendre plus de tout ce qui a changé.
Comment passer à Rails 2.0
Si vous voulez passer votre application à Rails 2.0, vous devez d’abord passer à Rails 1.2.3. Vous obtiendrez alors les avertissements de dépréciation pour presque tout ce qui a été supprimé de Rails 2.0. Donc, si votre application tourne correctement sous 1.2.3 sans avertissements de dépréciation, il y a de fortes chances qu’elle tourne sans problèmes avec rails 2.0. Bien entendu, si vous utilisez, par exemple la pagination, vous devrez installer le plugin classic_pagination. Si vous utilisez Oracle, vous devrez installer la gem activerecord-oracle-adapter. Et ainsi de suite pour toutes les extractions.
Pour installer la version de prévisualisation avec gems, faites:
gem install rails --source http://gems.rubyonrails.org
Pour l’essayer à partir d’un tag SVN, utilisez:
rake rails:freeze:edge TAG=rel_2-0-0_PR
Rails 1.2.4 qui avertira de quelques dépréciations supplémentaires sortira bientôt.
Dans tous les cas, il s’agit d’une version de prévisualisation. Utilisez la pour “sentir” la version 2.0. Voyez où vos applications ont besoin de changements. Et essayez de créer de nouvelles applications à partir de zéro pour voir les nouveaux paramètres par défaut. Dans quelques semaines, les version candidates seront publiées.
David conclut son article en remerciant tous ceux qui ont été impliqués dans le développement de Rails 2.0. Ils ont travaillé dessus depuis plus de six mois et il est heureux de pouvoir enfin partager ce travail avec un grand public. Enjoy!
2 Commentaires
Merci beaucoup Nicolas pour le temps passé à traduire le post de David.
Comme dit Rails 2 ne semble pas être une révolution mais un excellent cru, bien peaufiné, bien “clean architecturellement” parlant.
Excellent article.
Ca donne envie, vivement la version stable.
Au plaisir de te revoir.