Acesso mais fácil aos valores de configuração no Play! inscrição

Tudo começou quando eu estava tentando configurar um conjunto de tarefas agendadas para [Play! 2] [1], e isso me lançou em uma missão “descartar o boilerplate” na configuração do meu aplicativo.

Com o Play, você pode usar Akka.system.scheduler.schedulepara iniciar tarefas recorrentes em segundo plano em seu aplicativo da web. Para fazer isso, você precisa fornecer dois Durationque definem:
1. quando iniciar o agendamento
2. quanto tempo esperar antes de repetir a tarefa

Isso é algo que você não gostaria que fosse codificado, e o Play oferece [uma ótima maneira de ler seu application.conf] [2] arquivo e analisar durações. Na verdade, você pode escrever algo como

myapp.notifier.scheduleStart = 2 minutes
myapp
.notifier.scheduleEvery = 1 hour

E use:

import Play.current
Play.configuration.getMilliseconds("myapp.notifier.scheduleStart")

que retornará um Option[Long]que talvez contenha a configuração. Portanto, você também deve fornecer um padrão em seu código, portanto, obtenha realmente uma duração e acabará escrevendo:

import Play.current
import scala.concurrent.duration._
Play.configuration.getMilliseconds("myapp.notifier.scheduleEvery").getOrElse(30000l) milliseconds

O getOrElseexiste para fornecer o valor padrão caso você receba um Nonee o millisecondsconverte Longem um objeto de duração que pode ser compreendido por Akka.

Como você pode ver, tenho mais clichês do que o comprimento real da minha chave de configuração e meu valor padrão. O sistema de configuração do Play é legal, mas requer muita escrita. Se você tem muitas coisas para configurar em seu código, acaba com um código bastante confuso.

Mas alegre-se! Scala Magic ™ existe para nos salvar da RSI e do código que faz seu revisor querer matar alguém. Podemos primeiro escrever uma função em nosso escopo para fazer algo assim:

def configMilliseconds(key: String, default: FiniteDuration): FiniteDuration = 
Play.configuration.getMilliseconds(key).map(_.milliseconds).getOrElse(default)

Já está bem bacana, agora posso fazer … Não é bem mais curto? E não foi necessária nenhuma mágica ainda.configMilliseconds("myapp.notifier.scheduleEvery", 30 seconds)

Este código ainda tem um problema, ele requer um aplicativo de jogo implícito no escopo, também, acho que ele não coloca muita ênfase na origem da configuração. Seria legal focar no nome da chave de configuração, que tornaria nosso código ainda mais legível (mas essa é minha opinião, entendo que alguns prefiram a notação anterior). Tudo o que precisamos fazer é criar um operador postfix para uma chave de configuração, ou seja, a String.

Com o scala 2.10, isso é fácil (não é muito difícil com o scala 2.9 também), apenas escrevemos uma classe implícita que fornece as conversões para nós. Aqui está um que fornece o conjunto de tipos de configuração que mais uso:

import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration._
import play.api.Application
import collection.JavaConversions._

object ConfigString {
implicit class ConfigStr(s: String) {
def configOrElse(default: FiniteDuration)(implicit app: Application): FiniteDuration =
app
.configuration.getMilliseconds(s).map(_ milliseconds).getOrElse(default)

def configOrElse(default: Long)(implicit app: Application): Long =
app
.configuration.getMilliseconds(s).getOrElse(default)

def configOrElse(default: Double)(implicit app: Application): Double =
app
.configuration.getDouble(s).getOrElse(default)

def configOrElse(default: String)(implicit app: Application): String =
app
.configuration.getString(s).getOrElse(default)

def configOrElse(default: Boolean)(implicit app: Application): Boolean =
app
.configuration.getBoolean(s).getOrElse(default)

def configOrElse(default: Seq[String])(implicit app: Application): Seq[String] =
app
.configuration.getStringList(s).map(_.toSeq).getOrElse(default)
}
}

Agora eu posso apenas escrever: . Você pode ver que escolhi um nome extenso para a função postfix. Alguns podem preferir nomes mais curtos ou ir para símbolos malucos como Scalaz faz."myapp.notifier.scheduleEvery" configOrElse(30 seconds)

Existem algumas coisas a serem observadas neste código:

  • em vez de depender da importação de , considero o aplicativo como um argumento implícito, o que o torna um pouco mais versátilPlay.current
  • todas as funções têm o mesmo nome configOrElse, o scala descobre por conta própria qual tipo de retorno desejo a partir do valor padrão que forneço.
  • tudo o que preciso fazer agora para ter acesso às funções de configuração do postfix é import ConfigString._

Então, para agendar minha tarefa, antes:


val start
= Play.configuration.getMilliseconds("myapp.notifier.startQueuing").map(_.milliseconds).getOrElse(2 minutes)
val every
= Play.configuration.getMilliseconds("myapp.notifier.queueEvery").map(_.milliseconds).getOrElse(1 hour)

Akka.system.scheduler.schedule(start, every)
(NotificationScheduler.schedule)

depois de:


val start
= "myapp.notifier.startQueuing" configOrElse(2 minutes)
val every
= "myapp.notifier.synastry.queueEvery" configOrElse(1 hour)
Akka.system(app).scheduler.schedule(start, every)
(NotificationScheduler.schedule)

Finalmente, existe uma [essência] [3] para este código se você quiser estendê-lo ou torná-lo melhor.

[1]: http://www.playframework.com/
[2]: http://www.playframework.com/documentation/2.1.0/Configuration
[3]: https://gist.github.com/Mortimerp9 / 5249018