Le Blog du Geek Joyeux

Plus moins vite tu codes, moins plus vite ça plante

ruby

  • Définir une ressource Active Admin dans une gem

    Publié le 03 février 2012 à 00:00 - Général

    Si vous utilisez Active Admin pour générer vos interfaces d’administration sous Rails, voici une technique pour définir des ressources depuis un engine.

    Définir la ressource

    Pour une question de simplicité, je place mes ressources dans un dossier admin situé dans le dossier lib/my_engine de mon engine.

    # lib/my_engine/admin/articles.rb
    if defined?(ActiveAdmin)
    ActiveAdmin.register Article do
    # personnalisez ici votre ressource
    end
    end

    Charger la ressource

    Le problème auquel j’ai été confronté au début était celui du chargement de cette ressource.

    Naïvement, je l’avais placée dans app/admin/articles.rb dans mon engine mais elle n’était pas chargée par l’application. Je l’ai ensuite placée dans lib/my_engine/admin et ai appelé require dessus. Le problème est que le modèle Article n’était pas encore défini au moment de l’appel à register.

    J’ai donc fait appel à after_initialize dans la définition de l’engine:

    # lib/my_engine/engine.rb
    module MyEngine
    class Engine < Rails::Engine
    config.after_initialize do
    require 'my_engine/admin/articles'
    end
    end
    end

    Ce n’est peut être pas la solution la plus optimale ou la plus propre. Je serais d’ailleurs ravi d’avoir une solution alternative plus efficace. Celle-là a le mérite de fonctionner : ).

    Comments
  • size doesn't COUNT

    Publié le 17 janvier 2012 à 10:00 - Quick Tips

    Requêtes N+1

    Lorsque vous développez avec Rails, vous devez bien sûr faire attention aux requêtes qui sont effectuées et surtout éviter les requêtes N+1.

    Les requêtes N+1 sont celles qui sont lancées pour chaque élément d’une liste.

    Prenons l’exemple suivant :

    # Dans le contrôleur

    @articles = Article.all
    # SELECT articles.* FROM articles

    # Dans la vue

    <% @articles.each do |article| %>
    ...
    Nombre de commentaires: <%= articles.comments.count %
    >

    # SELECT COUNT(*) FROM comments WHERE comments.article_id = XY
    ...
    <% end %>

    Pour chaque article listé, une requête va être lancée pour compter ses commentaires.

    Heureusement pour nous, Rails fournit, dans l’API d’ActiveRecord un moyen d’éviter ces requêtes N+1. Il s’agit de la méthode includes qui s’utilise comme ceci :

    @articles = Article.includes(:comments).all
    # SELECT articles.* FROM articles
    # SELECT comments.* FROM comments WHERE comments.article_id IN (1, 2, ..., 42)

    On peut donc voir ici que l’ensemble des commentaires est récupéré d’un seul coup. Cela nous permet d’accéder aux informations des commentaires d’un article sans requête supplémentaire.

    Size vs. Count

    Le souci c’est que ça ne règle pas notre problème de count. En effet, si nous faisons de nouveau appel à count, les requêtes seront tout de même effectuées !

    L’astuce est donc d’utiliser size plutôt que count sur l’attribut comments de nos articles, ce qui va simplement retourner la taille du tableau contenant les commentaires associés. Ces derniers étant déjà chargés, le compte est bon !

    # Dans le contrôleur

    @articles = Article.includes(:comments).all
    # SELECT articles.* FROM articles
    # SELECT comments.* FROM comments WHERE comments.article_id IN (1, 2, ..., 42)

    # Dans la vue

    <% @articles.each do |article| %>
    ...
    Nombre de commentaires: <%= articles.comments.size %
    >

    ...
    <% end %>
    Comments
  • Versionner ses scripts de seeding avec versioned_seeds

    Publié le 07 décembre 2011 à 00:00 - Général

    Si vous vous retrouvez dans le besoin de faire des imports de données ou des insertions scriptées dans une application Rails, de nos jours vous n’avez pas vraiment d’autre choix que de créer des tâches rake (ou consorts) pour le faire.

    Dans cette idée, j’ai écrit la gem versioned_seeds qui permet de stocker ces scripts dans un dossier spécifique et de les utiliser à la manière des migrations.

    Son installation se fait grâce à l’ajout de la ligne suivante dans votre Gemfile :

    gem 'versioned_seeds', require: false

    Le principe est simple, vous appelez un générateur qui va créer au besoin le dossier et un script dans celui ci. Le nom du script commence par un timestamp, comme les migrations, ce qui va permettre à versioned_seeds de l’enregistrer et de ne pas l’exécuter plusieurs fois.

    rails g versioned_seeds:seed_file nom_du_script
    create db/seeds
    create db/seeds/20111207201942_nom_du_script.rb

    Vous n’avez plus qu’à écrire votre script puis, une fois terminé, à appeler une tâche rake qui va l’exécuter:

    rake vs:next

    Cette tâche va trouvé le prochain script non chargé jusqu’à présent et le lancer. Si vous avez généré plusieurs scripts, vous pouvez faire appel à

    rake vs:all

    Qui va les lancer un-à-un.

    Pour savoir où vous en êtes de vos scripts, vous pouvez faire appel à

    rake vs:status

    Pour connaitre la version du dernier script chargé.

    Pensez simplement à ignorer le fichier .versioned_seeds dans Git, il sert à stocker la liste des scripts déjà lancés et il ne doit pas être versionné puisqu’il est spécifique à chaque machine.

    Le code est disponible sur Github et la gem sur rubygems.org.

    Comments
  • Utiliser rvm avec cron

    Publié le 24 septembre 2011 à 00:00 - Quick Tips

    Il y a quelques temps, j’avais besoin de lancer un script ruby avec cron. Le souci, c’est que sur mon serveur, tout est à base de rvm. De fait, cron ne sait pas trouver ruby puisque rvm est chargé par mon fichier .bashrc.

    La petite astuce pour que ça marche, c’est de dire à cron de charger mon environnement pour ce script spécifiquement :

    0 * * * * /bin/bash -l -c 'ruby mon_script.rb'

    En clair, l’option -l permet de charger l’environnement bash et l’option -c indique la commande à exécuter.

    Comments
  • Installation de markItUp avec preview dans une application Rails3

    Publié le 02 juin 2011 à 00:00 - Général

    Il est fréquent d’avoir besoin d’un éditeur dit WYSIWYG dans un site web. Il en existe de deux types. Les éditeurs pur WYSIWYG où vous éditez directement du HTML en mode rendu final et les éditeurs de syntaxe qui se basent par exemple sur le format Wiki, Textile ou Markdown.

    markItUp est de la deuxième catégorie. Dans le cas d’un éditeur de syntaxe, il est toujours plus confortable de disposer d’une preview avant de valider ce qu’on a écrit.

    Voici un petit tutoriel sur comment installer markItUp dans une appli Rails3 en utilisant Sinatra pour générer la preview.

    Lire la suite

    Comments
  • La méthode inject en Ruby

    Publié le 21 avril 2011 à 00:00 - Quick tips

    En Ruby, il existe une méthode bien pratique nommée inject. Elle s’utilise de la manière suivante :

    puts (0..10).inject(0) do |somme, x|
    somme += x
    end
    # => 55

    Dans l’exemple précédent, on part d’une valeur de 0 que l’on modifie à chaque tour. Le code suivant est équivalent :

    somme = 0
    (0..10).each do |x|
    somme += x
    end
    puts somme
    # => 55

    De manière classique, on peut s’en servire pour générer un tableau ou une table de hash de la manière suivante :

    @articles.inject({}) do |hash, article|
    hash[article.title] = article.body
    end

    Une autre utilisation interessante, est avec les booléens :

    @plugins.inject(true) do |keep_going, plugin|
    keep_going &= plugin.some_method
    end

    On obtient donc en fin de parcours, true si tous les appels ont retourné true ou false si au moins un appel a retourné false.

    Dans le cas où on veut stoper l’appel à inject dés qu’un appel retourne false, on peut utiliser l’instruction break :

    @plugins.inject(true) do |keep_going, plugin|
    keep_going &= plugin.some_method or break(keep_going)
    end

    Cela permet de ne pas faire les appels suivants dés le premier retour négatif.

    Comments
  • Installer la gem mysql sous Snow Leopard

    Publié le 04 novembre 2010 à 13:17 - QuickTips

    Sous Snow Leopard, l’installation de la gem mysql n’est pas toujours simple. Voici ce que j’utilise, sachant que mon MySQL est installé via le DMG fourni sur le site officiel.

    export ARCHFLAGS="-arch i386 -arch x86_64";
    sudo gem install mysql -v2.7 --no-rdoc --no-ri -- \
    --with-mysql-dir=/usr/local/mysql \
    --with-mysql-config=/usr/local/mysql/bin/mysql_config
    Comments
  • Rails - Utiliser SASS avec theme_support

    Publié le 09 mars 2010 à 22:47 - Quick Tips

    J’ai eu l’occasion de me prendre un petit moment la tête aujourd’hui sur comment utiliser des feuilles de styles SASS au sein de thèmes dans une application Rails.

    Pour la gestion des thèmes, j’ai utilisé theme_support qui permet de créer, pour chaque thème, un dossier ayant l’arborescence suivante :

    un_theme
    ├── images
    | └── preview.png
    ├── javascripts
    ├── stylesheets
    ├── views
    └── about.markdown

    Ce que je cherchais à faire était de pouvoir mettre des fichiers SASS dans le dossier stylesheets, mais que les CSS compilées soient placées dans <app_root>/public/stylesheets.

    Le plus simple moyen que j’ai trouvé pour faire ça, est d’ajouter le script suivant dans >app_root>/config/initializers/theme_sass.rb :

    # :template_location est normalement une String, on la convertie donc en Array
    Sass::Plugin.options[:template_location] = [[Sass::Plugin.options[:template_location], Sass::Plugin.options[:css_location]]]

    # Pour chaque thème, on ajoute son dossier stylesheets à la liste des sources SASS
    Theme.find_all.map(&:name).each do |theme|
    Sass::Plugin.options[:template_location] << ["#{RAILS_ROOT}/themes/#{theme}/stylesheets", Sass::Plugin.options[:css_location]]
    end
    Comments
  • Utiliser GetText côté Javascript avec Rails

    Publié le 01 janvier 2010 à 21:08 - Général

    En travaillant sur une petite application Rails, j’ai utilisé GetText pour l’internationalisation. Jusque là, pas de problème… Sauf au moment où j’ai voulu traduire un message côté client (autrement dit Javascript).

    Je ne voulais pas faire d’appel AJAX pour chaque chaine à traduire.

    Tout naturellement, j’ai cherché un plugin simple qui fasse le travail mais n’ai rien trouvé qui me convienne.

    J’ai donc créé mon propre plugin, gettext_json. Voici comment l’utiliser.

    Lire la suite

    Comments
  • Utiliser Paperclip avec Rails sous Mac

    Publié le 28 décembre 2009 à 14:48 - Quick Tips

    Pour gérer l’upload de fichiers dans les applications Rails, un plugin fréquemment utilisé est Paperclip. Récemment, lors du développement d’un projet, je me suis retrouvé confronté à un problème lors de l’envoie des fichiers. En effet, ça ne fonctionnait pas et je recevais un message d’erreur à chaque fois.

    Erreur Paperclip

    Après quelques recherches, j’ai fini par trouver que cette erreur venait du fait que mon application ne trouvait pas son chemin vers les outils d’ImageMagick dont Paperclip dépend au travers de Rmagick.

    Pour résoudre ce problème, il suffit d’ajouter un initialiseur à l’application.

    J’ai donc ajouté le fichier RAILS_ROOT/config/initializers/paperclip.rb contenant ceci :

    Paperclip.options[:command_path] = '/opt/local/bin'

    J’ai utilisé ce chemin car j’ai installé ImageMagick en utilisant MacPorts.

    Comments
  • Connaitre les classes heritant d'une autre classe en Ruby

    Publié le 16 juillet 2008 à 18:01 - Général

    En Ruby, pour connaître la classe parente d’une classe, un appel de méthode suffit. Malheureusement, il n’existe pas de telle méthode afin d’obtenir les classes héritant d’une classe donnée. Voici donc comment créer ce comportement.

    Prérequis: une connaissance basique du langage Ruby est nécessaire pour comprendre certains listings de code.

    Lire la suite

    Comments