Publié le 25 Septembre 2015

Je viens de tomber sur un comportement du compilateur Scala auquel je ne m'attendais pas et qui peux s'avérer assez pernicieux si le développeur n'a pas conscience de ce fonctionnement.

 

Ceci concerne les pattern matching. Lorsqu'on "pattern match" sur un sealed trait le compilateur peut vérifier que tout les cas sont couvert. Par exemple:

 

sealed trait Record
case class InputRecord(input: String) extends Record
case class OutputRecord(output: String) extends Record

def logRecord(record: Record): Unit = record match {
  case in: InputRecord => println(s"Input record: $in")
  case out: OutputRecord => println(s"Output record: $out")
}

Là pas de problème le pattern match couvre les 2 sous-classes de Record et la compilation passe. Maintenant si j'oublie de traiter un cas:

 

def logRecord(record: Record): Unit = record match {
  case out: OutputRecord => println(s"Output record: $out")
}

Le compilateur le détecte est remonte une erreur car les InputRecord ne sont pas couvert. Super! C'est ce que j'attends du compilateur. Ce qui est plus surprenant maintenant est l'ajout d'une garde (un if quoi):

 

def logRecord(record: Record): Unit = record match {
  case out: OutputRecord if (!out.output.isEmpty) =>
    println(s"Output record: $out")
}

 

On est toujours dans le même cas que précédemment: les InputRecord ne sont pas traités. C'est même pire certains OutputRecord ne sont pas traités. Et pourtant scalac ne remonte aucune erreur, pas même un petit warning.

 

Je comprends bien que le compilateur ne peut pas analyser mon if mais il pourrait assumer que cela couvre les OutputRecord (tout au plus) et sortir une erreur pour les InputRecord (ou plus simplement afficher un warning comme quoi ce pattern match n'a pu être vérifier).

 

Pour plus d'info il y a un bug ouvert à ce sujet: https://issues.scala-lang.org/browse/SI-7631?jql=labels%20%3D%20pattern-matching

Voir les commentaires

Rédigé par Bliz

Publié dans #Scala

Repost 0

Publié le 8 Septembre 2015

Aujourd'hui je travaille sur une appli emberJS et j'ai un service REST qui retourne une structure JSON dont une des propriétés est du texte HTML.

Le fonctionnement par défaut d'Ember est d'échapper tout le contenu qui est rendu dans les template Handlebars. C'est une bonne chose d'un point de vue sécurité.

Seulement dans mon cas j'ai besoin de rendre ce texte non-échappé. (Attention je précise bien que ce contenu est généré uniquement par les administrateurs du site et non par les utilisateurs ce qui ouvrirait une énorme faille de sécurité).

Pour résoudre ce problème j'ai donc défini une propriété calculée sur mon modèle:

App.FaqEntry = DS.Model.extend({
   question: DS.attr('string'),
   answer: DS.attr('string'),
   htmlAnswer: function () {
      return
this.get('answer').htmlSafe();
   }.property('answer')
});

Ensuite il n'y a plus qu'à utiliser directement la propriété htmlAnswer dans le template:

<div>{{entry.htmlAnswer}}</div>

Voir les commentaires

Rédigé par Bliz

Publié dans #Javascript, #EmberJS

Repost 0

Publié le 8 Septembre 2015

Un des avantages de Playframework est qu'il affiche directement le code problématique dans le navigateur. C'est très pratique lors du développement d'une application mais pas vraiment ce qu'on désire une fois en prod.

Par ailleurs on peut vouloir afficher ses propres pages d'erreurs comme par exemple lors d'une page non trouvée.

En fait il est tout à fait possible de définir les comportements à utiliser en définissant un error handler, dans app/ErrorHandler.scala:

import javax.inject._
import play.api.http.DefaultHttpErrorHandler
import play.api._
import play.api.mvc._
import play.api.mvc.Results._
import play.api.routing.Router
import scala.concurrent._

class ErrorHandler @Inject() (
   env: Environment,
   config: Configuration,
   sourceMapper: OptionalSourceMapper,
   router: Provider[Router]
) extends DefaultHttpErrorHandler(env, config, sourceMapper, router) {

   override def onClientError(request: RequestHeader, statusCode: Int, message: String) = statusCode match {
      // 404 - Page non trouvée - on utilise notre propre page d'erreur
      case 404 => Future.successful(NotFound(views.html.Error.notFound()))
      // sinon on utilise le handler par défaut de Play
      case _ => super.onClientError(request, statusCode, message)
   }
}

Voir les commentaires

Rédigé par Bliz

Publié dans #Scala, #Play

Repost 0

Publié le 1 Septembre 2015

Je n'avais jusqu'ici que très peu utilisé handlebars (à la faveur d'angular) mais ce framework m'a plutôt agréablement surpris par sa simplicité et sa facilité d'extension.

C'est d'ailleurs cette fonctionnalité que nous allons utilisé pour définir notre propre formatage.

Handlebars.registerHelper('percentage', function (number) {
   // oui pas terrible comme formatage mais c'est juste pour l'exemple
   // on retourne par exemple "79.85 %"

   return (Math.round(number * 100) / 100) + " %";
});

Ensuite dans notre template on peut utiliser la "directive" "percentage" que nous venons de définir:

<p>Efficacité: {{percentage efficiency}}</p>

percentage est en fait la directive (helper) que nous venons de définir et efficiency est la variable contenant la valeur (pourcent) de l'efficacité.

Bien sûr il existe des librairies de formatage qui prennent en compte l'internationalisation (http://formatjs.io/handlebars) ce qui n'est pas le cas avec notre méthode ici. En revanche j'aime la simplicité de cette solution.

Voir les commentaires

Rédigé par Bliz

Publié dans #Javascript

Repost 0