Sunfox

Le journal de Sunny Ripert


Journal

Derniers articles de mon blog

Rails et Emails

Il y a 1 mois, 2 semaines en fin d'après-midi, 2 commentaires.

parisrb

À la dernière conférence Paris.rb je présentais 6 choses à arrêter de faire si vous envoyez des emails avec votre application Ruby on Rails.

Mes slides sont en ligne et voici mes 6 points :

  1. Arrêtez de les envoyer avec vos contrôleurs : crééez un contrôleur pour s’occuper de prévisualiser vos emails ou utilisez ActionMailer::Preview de Rails 4.1
  2. Arrêtez d’inliner les CSS à la main : utilisez la gemme premailer-rails
  3. Arrêtez de recharger votre navigateur : utilisez la gemme rack-livereload
  4. Arrêtez d’envoyer vos emails tout de suite : utilisez une système d’envoi en tâches de fond comme sidekiq et utilisez deliver_later disponible dans Rails 4.2.0.beta1
  5. Arrêtez d’envoyer des emails en développement : utilisez la gemme letter_opener
  6. Arrêtez de configurer vos URLs : utilisez ma gemme action_mailer_auto_url_options, disponible sur Github et publiée sur Rubygems depuis aujourd’hui !

À ces 6 raisons, Loïc Boutet ajoute dans les commentaires à la fin une 7ème chose à ne plus faire lorsque vous envoyez des emails avec votre application Rails : configurer son serveur SMTP. Utilisez plutôt un service externe comme MailJet ou Mandrill.

PS : Voici la vidéo de ma présentation.

PS : voici une photo de mon chat :

Peach is fed up with the bubbles

Comment j’ai rendu mon blog responsive

Il y a 2 mois, 1 semaine de bonne matinée, 5 commentaires.

L’HTML et le CSS de ce blog ont été écrits en 2006. En années Internet c’est comme si je l’avais fait y a 3042 ans.

À cette époque l’iPhone n’existait pas encore et on ne se préoccupait pas de savoir si un site fonctionnait sur son assistant personnel ou pas. La première mention de Responsive Web Design date de 2010 (il y a 520 ans environ). Essentiellement il s’agit de transformer un site pour qu’il puisse s’adapter de façon fluide à toutes tailles d’écrans.

Transformer quelque chose qui n’a pas été pensé mobile first n’est pas chose facile, alors un vieux site au CSS croupissant, cela paraît insurmontable.

Pourtant, il ne m’a fallu ajouter que les quelques règles de style suivantes :

body { max-width: 55em; } /* (au lieu de width: 55em) */
img { max-width: 100%; }
textarea { max-width: 100%; }

Et l’HTML :

<meta name="viewport" content="width=device-width" />

Capture de Sunfox.org sur un Nexus 5

Et voilà !

Si c’était facile c’est parce que le site lui-même était très simple, sans grille, en une seule colonne, avec peu de largeurs fixes. Je n’ai eu qu’à régler le problème de largeur maximale de la page et profiter du fait qu’HTML et CSS sont flexibles par défaut.

Le tout premier site, à l’origine du World Wide Web, a été remis en place à son URL originale. C’est un site encore plus simple. Sa simplicité fait qu’il fonctionne très bien sur un smartphone, de façon flexible, sans avoir à zoomer, sans avoir à ajouter des styles CSS ni des breakpoints.

Tout ça alors qu’il a été créée en 1989 (il y a 20 000 années Internet environ).

Capture d'écran du tout premier site Web, sur un smartphone

Pour avoir des sites future-friendly qui résistent aux temps et aux technologies, faites comme ce dernier, adoptez la simplicité.

En CSS, n’utilisez que des classes

Il y a 2 mois, 2 semaines le soir, 2 commentaires.

Photo de la mer

CSS permets d’utiliser plein de sélecteurs raffinés pour ajouter du style à vos éléments. Je soutiens qu’il faut éviter autant que possible d’utiliser quoi que ce soit d’autre que le sélecteur de classes (.foo).

Pour que vos styles soit réutilisables et ne soit plus aussi liés à votre HTML fini donc d’utiliser des sélecteurs d’identifiants, de balises, d’attributs ou de parenté.

Voici pourquoi.

Identifiants

#content { color: red; }

Les sélecteurs par ID vous empêchent de réutiliser vos styles, tout simplement.

Ils ont un autre inconvénient important : leur spécificité est extrèmement importante. C’est le début d’une lutte pour la spécificité si on veut remplacer des styles. On se retrouve avec des sélecteurs longs et complexes comme .page.about-page #content.important { color: red; }.

Balises

h1 { color: grey; }
.side ul { margin: 0; }

Ces sélecteurs sont trop généralistes. Si jamais vous utilisez un h1 autre part sur votre site ou que vous voulez une liste différente dans .side vous devrez commencer par annuler tous les styles.

Conséquence : vos CSS seront plus lourdes, se répèteront, vos styles ne seront pas indépendants les uns envers les autres et le tout sera plus difficiles à maintenir à l’avenir.

Attributs

[disabled] { opacity: .5; }
[data-triggered="true"] { background: green; }

Comme ces attributs sont sémantiques ils souffrent du même problème que les balises, impossible de réutiliser les styles associés sur d’autres éléments. Par exemple un bouton peut être disabled mais pas un lien qui aurait un style de bouton.

En terme de performances également : pour ne pas ralentir les pages avec beaucoup d’éléments DOM il ne faut pas utiliser les attributs seuls mais toujours y coller au moins une classe, un nom de balise ou un sélecteur enfant (.btn[disabled], input[disabled] ou [disabled] .btn). Raison de plus de les éviter.

Parenté

.about-page .sidebar {}
.sidebar + .article {}
.article > div {}

Les sélecteurs descendants, adjacents et enfants sont également à éviter car ils ont une très grande dépendance sur votre HTML. Cela veut dire que changer l’HTML implique de changer votre CSS. Ils seront également très peu réutilisables.

Il est difficile de se séparer du sélecteur descendant, mais il faut essayer de le minimiser, surtout en SCSS où c’est si facile d’indenter des sélecteurs.

Universel

* { margin: 0; }

En plus d’être un trou à performances, ce sélecteur touche trop d’éléments et oblige souvent d’écraser les styles appliqués. Cela n’aide pas à ce que le CSS généré soit réutilisable facilement, ou qu’il n’impacte pas le reste du site.

Classes

.foo {}

Les classes en revanche peuvent être réutilisées, déplacées et ne dépendent pas de la sémantique de votre document.

En utilisant uniquement des classes vous pourrez réutiliser vos styles quelle que soit la sémantique. Votre .cool-bloc pourra être appliqué sur une div, une section, un form ou une nav selon l’utilité de ce bloc.

Ajoutez la couleur dominante derrière vos images

Il y a 2 mois, 4 semaines en fin de matinée, 1 commentaire.

Si vous pouvez extraire la couleur dominante d’une photo, une petite touche sympa à ajouter sur votre site est de se servir de celle-ci tant que votre image n’est pas chargée.

Voici un exemple sur cults3d.com :

Chargement d'une page sur Cults 3D

HTML

En HTML, le plus simple est d’appliquer la couleur dominante sur son style inline. C’est cette couleur qui apparaîtra durant le chargement

<img style="background: #f3de56" src="…" width="120" height="120" />

Avec Rails

Si vous utilisez la librairie Carrierwave dans votre application Rails, vous pouvez extraire facilement la couleur dominante à chaque upload grâce à ma gemme ruby carrierwave-processing-dominant_color.

Avec WordPress

Si vous utilisez WordPress, ajoutez l’extension dominant-color et ça sauvegardera la couleur dominante à chaque upload de medias. Vous pourrez même éditer la couleur choisie, directement dans l’administration.

EasouSpider le robot qui ne sait pas parler l’utf8

Il y a 3 mois de bonne matinée.

Photo de Mathieu mangeant une araignée

Ces derniers jours je recevait une trentaine d’emails par jour m’indiquant qu’une erreur avait éclatée sur cults3d.com. Ces exceptions provenaient toutes d’un robot chinois, EasouSpider, qui parcoure le Web pour remplir son moteur de recherche.

Ce robot ne sait pas parler l’unicode comme tout le monde et envoie dans les formulaires des valeurs mal encodées, ce qui fâche Ruby on Rails. Le problème est surtout que Rails ne traite l’erreur que bien trop tard, lorsqu’on essaie de lire les paramètres de la requête. Et là l’erreur est une incompréhensible ArgumentError :

invalid %-encoding (Q/B0*ÜHܘ0ÅÅ1 0 U GB1 0 U.…

La solution est de créer un middleware Rack qui va déclencher et traiter cette erreur avant que le reste de l’application ne reçoive la requête. Rails est fait de tas de petits modules comme celui-ci qui s’empilent et qui préparent la requête.

Pour ajouter facilement ce middleware à mon application j’ai créé une gemme Ruby, avec un nom particulièrement court. Pour régler ce problème dorénavant, il suffit d’ajouter à son Gemfile :

gem "handle_invalid_percent_encoding_requests"

Depuis, plus aucun message d’erreur. Je dors beaucoup mieux la nuit.

Retrouver les clefs de localisation facilement avec Rails

Il y a 3 mois en début d'après-midi, 2 commentaires.

Photo de Drapeaux Lego

J’utilise Locale avec Ruby on Rails pour permettre à n’importe qui dans l’équipe de modifier les textes d’un site quelle que soit la langue.

Néanmoins, ce n’est pas évident pour celui qui n’a pas le code sous les yeux de retrouver quel texte correspond à quelle clef.

L’Astuce

Une petite astuce est d’ajouter à son ApplicationController la méthode suivante :

# Print out the keys if you add `?_locale_keys=1` in your params
def t(key, options = {})
  if !Rails.env.production? and request and params[:_locale_keys]
    scope_key_by_partial(key)
  else
    super
  end
end

Cela vous permet d’afficher toutes les clefs d’une page en ajoutant le paramètre _locale_keys à vos URLs, par exemple : http://example.com/?_locale_keys=1.

Voici un exemple sur cults3d.com où chaque texte est remplacé par sa clef :

Le site web Cults3D avec Locale Keys allumé

Sécurité

Il n’y a pas de risque de permettre à tout le monde d’afficher les clefs de localisation. Néanmoins par pudeur et pour éviter que ces pages disgracieuses n’apparaissent dans un moteur de recherche, on teste que l’environnement soit celui de production.

Vous pouvez également remplacer ce test par un test qui vérifie que l’utilisateur est un administrateur, par exemple.

ActionMailer n’aime pas vos URLs (par défaut)

Il y a 3 mois, 1 semaine à l'heure du goûter.

Faire un lien dans un email dans une application Ruby on Rails devrait être aussi simple que de faire un lien dans le reste de l’application, non ?

Photo of a miniature train, by Sunny

Problème

Mais voilà, un simple link_to root_url renvoie une exception car il faut un nom de domaine pour faire une URL. Pour ça il faut configurer un nom de domaine par défaut pour chaque environnement.

En développement un site varie beaucoup selon les configurations et les tests. Par exemple foo.dev, localhost, sunny.local et 192.168.1.42.foo.xip.io, sont des noms de domaines possibles et utiles pour mon application en développement. Le port (80, 3000, 8080) et le protocole (http ou https) sont également des paramètres qui peuvent changer lors des différents tests.

En production votre nom de domaine peut également varier. Par exemple en.cults3d.com et fr.cults3d.com sont deux noms de domaines utilisés par la même application, en même temps. Il est donc impossible de trouver un nom de domaine par défaut à utiliser sur chaque lien.

C’est pénible de devoir spécifier le domaine à chaque fois, c’est source de bugs.

Solution

Pour éviter cette configuration « en dur » il existe une manipulation simple qui consiste à modifier l’URL par défaut à chaque requête.

En ajoutant à son application_controller.rb :

before_filter :make_action_mailer_use_request_host_and_protocol
def make_action_mailer_use_request_host_and_protocol
  ActionMailer::Base.default_url_options[:protocol] = request.protocol
  ActionMailer::Base.default_url_options[:host] = request.host_with_port
end

Sécurité

Attention, cette solution ne vous prémunit pas contre le host injection exploit.

Si votre site déclenche un email (demande de mot de passe par exemple) et qu’un méchant indique comme hôte « evil.com » alors vous allez envoyer des emails ayant des liens en « evil.com ».

Si votre site utilise du cache ces attaques touchent sans doute déjà votre application ! Pour s’en prémunir, ajoutez donc un filtre des noms de domaines autorisés en production.

Thread Safety

Cette solution (corrigez-moi si je me trompe) souffre d’un problème de race condition. Non, ce n’est pas un problème de racisme envers certains noms de domaines qu’elle ne peut pas blairer.

Le problème est plutôt qu’une requête plus lente peut modifier sans le vouloir le domaine d’une autre requête. Une meilleure solution thread-safe existe sans doute. Si vous avez une piste élégante je suis preneur.

Gem ça

Et parce qu’une ligne de code testée dans une librairie dédiée vaut mieux que 2 lignes copié-collées dans votre projet, vous pouvez également ajouter ce code grâce à ma petite gemme :
action_mailer_auto_url_options.

Photo of a miniature train, by Sunny

Liens de partage sans JavaScript vers Facebook, Twitter, Google Plus, Pinterest et email

Il y a 8 mois, 2 semaines le soir, 1 commentaire.

Exemple de boutons de partage de Cults3d.com

Parce que vous voulez inclure des liens de partage sans que ça ne vienne importer 1 Mo de JavaScript sur votre page, voici des liens de partages. Sans balise script. Rien que des liens. Simple.

Ne pas oublier d’URL-encoder chaque paramètre, comme dans les exemples.

Facebook

URL : https://www.facebook.com/sharer/sharer.php?u={u}

Paramètres :

  • u : URL à partager

Exemple : https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2F…

Facebook dialog

Une boîte de dialogue avec plus d’options que le partage « simple » mais qui a pour inconvénient de devoir créer une application au préalable.

URL : https://www.facebook.com/dialog/feed?app_id={app_id}&link={link}&picture={picture}&name={name}&caption={caption}&description={description}&redirect_uri={redirect_uri}

Paramètres :

  • app_id : numéro unique d’application Facebook
  • link : URL de la page en cours
  • picture : URL d’une image
  • name : nom de la page
  • caption : sous-titre de la page
  • description : description de la page
  • redirect_uri : URL pour revenir vers votre page, celle de la page en cours par exemple

Exemple : https://www.facebook.com/dialog/feed?link=http%3A%2F%2Fsu…

Twitter

URL : http://twitter.com/intent/tweet/?url={url}&text={text}&via={via}

Paramètres :

  • url : URL à partager
  • text : message par défaut du tweet
  • via : compte Twitter pour ajouter un « via @sunfox » par exemple à la fin (optionnel)

Exemple : http://twitter.com/intent/tweet/?url=http%3A%2F%2Fsunfox.…

Google Plus

URL : https://plus.google.com/share?url={url}&hl={hl}

Paramètres :

  • url : URL à partager
  • hl : Code langue pour la page de partage (optionnel)

Exemple : https://plus.google.com/share?url=http%3A%2F%2Fsunfox.org%…

Pinterest

URL : http://pinterest.com/pin/create/button/?url={url}&media={media}&description={description}

Paramètres :

  • url : URL à partager
  • media : URL de l’image à partager
  • description : description par défaut

Exemple : http://pinterest.com/pin/create/button/?url=http%3A%2F%2Fs…

Email

URL : mailto:?subject={subject}&body={body}

Paramètres :

  • subject : Sujet par défaut de l’email
  • body : Message par défaut de l’email

Exemple : mailto:?subject=Liens%20de%20partage%20sans%20JavaScript%…

Favicon dynamique

Il y a 1 an, 4 mois le soir, 1 commentaire.

Capture d'écran de favicônes toutes identiques
Sites sans favicon : Boo!

Le favicon est un endroit assez délaissé dans une page Web alors que c’est parfois la seule indication visuelle permettant aux utilisateurs de de se retrouver dans ses onglets.

Edith

Sur Edith j’ai choisi de représenter la page en cours directement dans le favicon, en le mettant à jour à chaque caractère tapé.

Capture d'écran montrant la modification en temps réel de la favicon
favicon qui change à la volée sur Edith

JavaScript

Trop facile.

  1. Créer un canvas de 16×16 et colorier ses pixels via JavaScript
  2. Appeler sa méthode toDataURL() pour transformer le canvas en data:image/png;base64,iVBORw0…
  3. Placer dans le DOM dans le <link rel="icon" href="…" /> de la page en cours.

Noter que pour qu’il soit mis à jour instantanément Firefox a besoin qu’on supprime et qu’on recréer la balise link.

Exemple dans le CoffeeScript utilisé sur Edith.

Can I Use?

Fonctionne là où canvas et les datauri fonctionnent, soit IE9+.

Erreurs 404 dynamiques dans Rails 3

Il y a 1 an, 11 mois de bonne matinée.

Utiliser un fichier statique public/404.html dans Rails est très vite limitant. Si on veut utiliser le layout normal de l’application, des informations dynamiques comme des menus, le nom de l’utilisateur courant ou un formulaire de recherche il faut rendre ces erreurs dynamiques.

Pour ça il faut intercepter deux erreurs qui produisent des 404 : les infos nos trouvées dans la base via ActiveRecord::RecordNotFound et les routes qui n’existent pas avec ActionController::RoutingError.

RecordNotFound

Pour que les exceptions levées par ActiveRecord qui retournent une 404 soient gérées de façon dynamique il faut ajouter un gestionnaire d’exception :

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :error_404

  private
    def error_404
      respond_to do |type|
        type.html { render status: 404, template: "errors/404" }
        type.all  { render status: 404, nothing: true }
      end
    end
end

On peut maintenant ajouter tout ce qu'on veut dans views/errors/404.html.erb. Sauvés !

RoutingError

Pas moyen de les intercepter au niveau du contrôleur ces erreurs là. Il faut donc créer une route générique à la main tout en bas du config/routes.rb :

match "*path", to: "errors#not_found"

Et un contrôleur qui va gérer cette nouvelle route :

# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
  def not_found
    error_404
  end
end

Erreurs 500 ?

N'utilisez pas ça pour les erreurs 500, contentez-vous d'un public/500.html ou votre application et Internet avec risquent d'imploser. Si vous voulez quelquechose de simili-dynamique vous pouvez utiliser un générateur de pages statiques comme snow.


:D Sunny Ripert

est un développeur web vivant à Paris.

CV, me contacter


Textes et contenus sous licence Creative Commons.
Site crée par mes soins et propulsé par WordPress.