Le Rayon UX

La radiographie du Web en temps presque réel / thème en chantier (je m'appelle Teuse)

Ta mère elle référence des string ruby devant la piscine de Maubeuge

Sous ce titre primesautier se dissimule un problème idiot rencontré hier soir sur le développement de Typo que j’aurais certainement résolu plus rapidement sans un abus de mojitos.

Je voulais modifier la manière dont Typo transforme les URL en liens permanents en supprimant les caractères spéciaux et accentués. J’ai donc fait le bout de code suivant :


def stripped_title
  str = self.title
  
  accents = { ['á','à','â','ä','ã','Ã','Ä','Â','À'] => 'a',
    ['é','è','ê','ë','Ë','É','È','Ê'] => 'e',
    ['í','ì','î','ï','I','Î','Ì'] => 'i',
    ['ó','ò','ô','ö','õ','Õ','Ö','Ô','Ò'] => 'o',
    ['œ'] => 'oe',
    ['ß'] => 'ss',
    ['ú','ù','û','ü','U','Û','Ù'] => 'u',
    ['ç','Ç'] => 'c'
    }
  accents.each do |ac,rep|
    ac.each do |s|
      str.gsub!(s, rep)
    end
  end
  
  str.gsub(/<[^>]*>/,'').to_url
end

À priori, ça semble correct. Malheureusement, une fois la fonction exécutée, les accents ont été retirés et de str, ce qui est normal, et de self.title, ce qui l’est moins. Une autre manifestation du double effet kiss cool diront certains, mais surtout un oubli manifeste de la nature profondément objet de Ruby.

Que s’est-il passé ?

Lorsque j’ai déclaré str = self.title, je n’ai pas copié le contenu de la chaîne dans la variable str, mais j’ai fait pointer str vers self.title. Résultat, toute modification de la première entraînait obligatoirement la modification de la seconde. Pour avoir le résultat escompté j’aurais du déclarer str = String.new(self.title). Ça m’a rappelé le C et mes premiers exercices avec les pointeurs. Souvenirs.

Le code correct est donc :

def stripped_title
  str = String.new(self.title)
  
  accents = { ['á','à','â','ä','ã','Ã','Ä','Â','À'] => 'a',
    ['é','è','ê','ë','Ë','É','È','Ê'] => 'e',
    ['í','ì','î','ï','I','Î','Ì'] => 'i',
    ['ó','ò','ô','ö','õ','Õ','Ö','Ô','Ò'] => 'o',
    ['œ'] => 'oe',
    ['ß'] => 'ss',
    ['ú','ù','û','ü','U','Û','Ù'] => 'u',
    ['ç','Ç'] => 'c'
    }
  accents.each do |ac,rep|
    ac.each do |s|
      str.gsub!(s, rep)
    end
  end
  
  str.gsub(/<[^>]*>/,'').to_url
end

Sur la tour Eiffel

  • Par Eric 04/10/2007 at 22h38

    Il y a plus simple : l’utilisation d’iconv

    Tu convertis de ton codage classique vers US-ASCII avec l’option //TRANSLIT

    Ca te fera sauter tous tes accents et tes caractères non ascii, avec pile ce qu’il faut.

    Comme ça tu n’oublieras pas les majuscules accentuées, les cédilles, les tilda, etc.


  • Par FlorentG 04/10/2007 at 23h34

    En Java c’est pareil. Ou pour tout langage où les Strings sont des objets passés par référence.

    @Eric : j’ai fait pareil. C’est, je pense, la meilleure solution.

    Je fais d’abord une conversion utf-8 vers utf-8 avec //IGNORE pour nettoyer la string de tout caractère invalide/chelou. Puis vers US-ASCII en //TRANSLIT. Ensuite je convertis tout en minuscule, je remplace certains signes de ponctuation par des tirets. Et voilà :)


  • Par Strass 05/10/2007 at 03h13

    Je m’oppose formellement à l’utilisation d’iconv ! En effet, ça part d’un bon sentiment : un code clean de quelques lignes qui fait bien le boulot. Sauf qu’entre une Ubuntu Gutsy et mon serveur sous Debian Etch (quand même pas la mer à boire), ça ne donne pas du tout le même résultat ! Il y a sans doute une solution super géniale et sans doute très bête à ce comportement, mais avec l’utilisation d’une table de conversion comme le fait Frédéric, on est sur d’arriver au résultat escompté.


  • Par Olivier Bonnaure 05/10/2007 at 09h04

    Pour ce probl�me de pointeur, j’utilise toujours la fonction clone de Object. On aurait du donc pu faire aussi : str = self.title.clone


  • Par FlorentG 05/10/2007 at 09h10

    @Strass : ouais c’est sûr, ça peut varier, maintenant dans mon cas précis, j’ai que deux environnement à gaffer : win32 et un linux. Aussi, ne pas oublier de définire la locale (du moins sous PHP), un setlocale(‘fr_fr’) permet déjà d’avoir un comportement assez similaire d’une machine à l’autre.


  • Par pouype 05/10/2007 at 09h24

    AAahh, on va avoir des corrections totomatique des url dans le prochain typo !!! :D coool

    Sinon attention, en java les strings on un comportement particulier, pas forcement très objets :p


  • Par Eric 05/10/2007 at 16h44

    @Strass:

    Moi je m’oppose formellement à toute interprétation qui ne contient pas le é majuscule, le à, ou le ç majuscule. Je les utilise régulièrement quand je fais de l’écrit “réfléchi”. Comme il m’arrive de parler italien j’attend aussi les ì, ò et ù. J’impose aussi les lettres composés (e dans le o par exemple)

    Le problème c’est que je ne fais pas confiance à grand monde pour ce qui est de penser à toutes les combinaisons (même en se limitant à l’europe de l’ouest). Dans une conversion en liste noire, on est sûr d’en oublier. Des gens ont très bien fait le boulot dans une bibliothèque intégrée par défaut, ça serait du gachi de ne pas l’utiliser. (et je ne parle même pas des problèmes de perf sur des gsub multiples)


  • Par Moe 05/10/2007 at 23h55

    Et Œ ? Et les autres caractères $ , : . & # ! ? * ©, comment sont-ils traités ?


  • Par Strass 09/10/2007 at 12h21

    @Eric

    En fait, j’étais partie sur la transformation iconv, j’étais très fier du code : en lignes tout était fait. Mais en déployant en prod, j’ai remarqué que le comportement était un poil différent… J’en suis donc arrivé à la solution de la table de hache… Faudra que je me repenche sur le problème.


  • Par Thibaut Barrère 30/01/2009 at 23h25

    Iconv peut effectivement échouer selon les plateformes…

    La lib diacriticsfu (github.com/thbar/diacriticsfu/tree) ou la méthode parameterize (dans Rails récent) peuvent gérer la suppression des diacritiques de façon portable.


Commentaire Ta mère elle référence des string ruby devant la piscine de Maubeuge