Remove unsafe DB accesses to add monad crazyness
This commit is contained in:
parent
1dd772b5b3
commit
e2379dbc22
138
app/AppApi.scala
138
app/AppApi.scala
|
@ -21,47 +21,62 @@ final class AppApi(
|
|||
messenger: Messenger,
|
||||
starter: Starter) {
|
||||
|
||||
private implicit val timeout = Timeout(200 millis)
|
||||
private implicit val timeout = Timeout(300 millis)
|
||||
private implicit val executor = Akka.system.dispatcher
|
||||
|
||||
def show(fullId: String): Future[IO[Map[String, Any]]] =
|
||||
def show(fullId: String): Future[IO[Valid[Map[String, Any]]]] =
|
||||
(gameHubMemo getFromFullId fullId) ? game.GetVersion map {
|
||||
case game.Version(version) ⇒ for {
|
||||
pov ← gameRepo pov fullId
|
||||
roomHtml ← messenger render pov.game.id
|
||||
} yield Map(
|
||||
"version" -> version,
|
||||
"roomHtml" -> roomHtml,
|
||||
"possibleMoves" -> {
|
||||
if (pov.game playableBy pov.player)
|
||||
pov.game.toChess.situation.destinations map {
|
||||
case (from, dests) ⇒ from.key -> (dests.mkString)
|
||||
} toMap
|
||||
else null
|
||||
}
|
||||
)
|
||||
povOption ← gameRepo pov fullId
|
||||
gameInfo ← povOption.fold(
|
||||
pov ⇒ messenger render pov.game.id map { roomHtml ⇒
|
||||
Map(
|
||||
"version" -> version,
|
||||
"roomHtml" -> roomHtml,
|
||||
"possibleMoves" -> {
|
||||
if (pov.game playableBy pov.player)
|
||||
pov.game.toChess.situation.destinations map {
|
||||
case (from, dests) ⇒ from.key -> (dests.mkString)
|
||||
} toMap
|
||||
else null
|
||||
}
|
||||
).success
|
||||
},
|
||||
io(GameNotFound)
|
||||
)
|
||||
} yield gameInfo
|
||||
}
|
||||
|
||||
def join(
|
||||
fullId: String,
|
||||
url: String,
|
||||
messages: String,
|
||||
entryData: String): IO[Unit] = for {
|
||||
pov ← gameRepo pov fullId
|
||||
p1 ← starter.start(pov.game, entryData)
|
||||
p2 ← messenger.systemMessages(p1.game, messages) map { evts ⇒
|
||||
p1 + RedirectEvent(!pov.color, url) ++ evts
|
||||
}
|
||||
_ ← gameRepo save p2
|
||||
_ ← gameSocket send p2
|
||||
} yield ()
|
||||
entryData: String): IO[Valid[Unit]] = for {
|
||||
povOption ← gameRepo pov fullId
|
||||
op ← povOption.fold(
|
||||
pov ⇒ for {
|
||||
p1 ← starter.start(pov.game, entryData)
|
||||
p2 ← messenger.systemMessages(p1.game, messages) map { evts ⇒
|
||||
p1 + RedirectEvent(!pov.color, url) ++ evts
|
||||
}
|
||||
_ ← gameRepo save p2
|
||||
_ ← gameSocket send p2
|
||||
} yield success(),
|
||||
io(GameNotFound)
|
||||
)
|
||||
} yield op
|
||||
|
||||
def start(gameId: String, entryData: String): IO[Unit] = for {
|
||||
g1 ← gameRepo game gameId
|
||||
progress ← starter.start(g1, entryData)
|
||||
_ ← gameRepo save progress
|
||||
_ ← gameSocket send progress
|
||||
} yield ()
|
||||
def start(gameId: String, entryData: String): IO[Valid[Unit]] =
|
||||
gameRepo game gameId flatMap { gameOption ⇒
|
||||
gameOption.fold(
|
||||
g1 ⇒ for {
|
||||
progress ← starter.start(g1, entryData)
|
||||
_ ← gameRepo save progress
|
||||
_ ← gameSocket send progress
|
||||
} yield success(Unit),
|
||||
io { !!("No such game") }
|
||||
)
|
||||
}
|
||||
|
||||
def rematchAccept(
|
||||
gameId: String,
|
||||
|
@ -70,32 +85,47 @@ final class AppApi(
|
|||
whiteRedirect: String,
|
||||
blackRedirect: String,
|
||||
entryData: String,
|
||||
messageString: String): IO[Unit] = for {
|
||||
messageString: String): IO[Valid[Unit]] = for {
|
||||
color ← ioColor(colorName)
|
||||
newGame ← gameRepo game newGameId
|
||||
g1 ← gameRepo game gameId
|
||||
progress = Progress(g1, List(
|
||||
RedirectEvent(White, whiteRedirect),
|
||||
RedirectEvent(Black, blackRedirect),
|
||||
// to tell spectators to reload the table
|
||||
ReloadTableEvent(White),
|
||||
ReloadTableEvent(Black)))
|
||||
_ ← gameRepo save progress
|
||||
_ ← gameSocket send progress
|
||||
newProgress ← starter.start(newGame, entryData)
|
||||
newProgress2 ← messenger.systemMessages(
|
||||
newProgress.game, messageString
|
||||
) map newProgress.++
|
||||
_ ← gameRepo save newProgress2
|
||||
_ ← gameSocket send newProgress2
|
||||
} yield ()
|
||||
newGameOption ← gameRepo game newGameId
|
||||
g1Option ← gameRepo game gameId
|
||||
result ← (newGameOption |@| g1Option).tupled.fold(
|
||||
games ⇒ {
|
||||
val (newGame, g1) = games
|
||||
val progress = Progress(g1, List(
|
||||
RedirectEvent(White, whiteRedirect),
|
||||
RedirectEvent(Black, blackRedirect),
|
||||
// tell spectators to reload the table
|
||||
ReloadTableEvent(White),
|
||||
ReloadTableEvent(Black)))
|
||||
for {
|
||||
_ ← gameRepo save progress
|
||||
_ ← gameSocket send progress
|
||||
newProgress ← starter.start(newGame, entryData)
|
||||
newProgress2 ← messenger.systemMessages(
|
||||
newProgress.game, messageString
|
||||
) map newProgress.++
|
||||
_ ← gameRepo save newProgress2
|
||||
_ ← gameSocket send newProgress2
|
||||
} yield success()
|
||||
},
|
||||
io(GameNotFound)
|
||||
): IO[Valid[Unit]]
|
||||
} yield result
|
||||
|
||||
def reloadTable(gameId: String): IO[Unit] = for {
|
||||
g1 ← gameRepo game gameId
|
||||
progress = Progress(g1, Color.all map ReloadTableEvent)
|
||||
_ ← gameRepo save progress
|
||||
_ ← gameSocket send progress
|
||||
} yield ()
|
||||
def reloadTable(gameId: String): IO[Valid[Unit]] = for {
|
||||
g1Option ← gameRepo game gameId
|
||||
result ← g1Option.fold(
|
||||
g1 ⇒ {
|
||||
val progress = Progress(g1, Color.all map ReloadTableEvent)
|
||||
for {
|
||||
_ ← gameRepo save progress
|
||||
_ ← gameSocket send progress
|
||||
} yield success()
|
||||
},
|
||||
io(GameNotFound)
|
||||
)
|
||||
} yield result
|
||||
|
||||
def gameVersion(gameId: String): Future[Int] =
|
||||
(gameHubMemo get gameId) ? game.GetVersion map {
|
||||
|
|
|
@ -7,14 +7,9 @@ import akka.util.duration._
|
|||
import akka.util.{ Duration, Timeout }
|
||||
import scalaz.effects._
|
||||
|
||||
import socket.GetNbMembers
|
||||
import site.{ NbMembers, WithUsernames }
|
||||
import lobby.WithHooks
|
||||
import RichDuration._
|
||||
|
||||
final class Cron(env: SystemEnv)(implicit app: Application) {
|
||||
|
||||
implicit val timeout = Timeout(200 millis)
|
||||
implicit val timeout = Timeout(500 millis)
|
||||
implicit val executor = Akka.system.dispatcher
|
||||
|
||||
message(2 seconds) {
|
||||
|
@ -22,11 +17,11 @@ final class Cron(env: SystemEnv)(implicit app: Application) {
|
|||
}
|
||||
|
||||
message(1 second) {
|
||||
env.lobbyHub -> WithHooks(env.hookMemo.putAll)
|
||||
env.lobbyHub -> lobby.WithHooks(env.hookMemo.putAll)
|
||||
}
|
||||
|
||||
message(2 seconds) {
|
||||
env.siteHub -> NbMembers
|
||||
env.siteHub -> site.NbMembers
|
||||
}
|
||||
|
||||
effect(2 seconds) {
|
||||
|
@ -38,7 +33,7 @@ final class Cron(env: SystemEnv)(implicit app: Application) {
|
|||
}
|
||||
|
||||
message(3 seconds) {
|
||||
env.siteHub -> WithUsernames(env.userRepo.updateOnlineUsernames)
|
||||
env.siteHub -> site.WithUsernames(env.userRepo.updateOnlineUsernames)
|
||||
}
|
||||
|
||||
effect(2 hours) {
|
||||
|
@ -53,6 +48,8 @@ final class Cron(env: SystemEnv)(implicit app: Application) {
|
|||
env.remoteAi.diagnose
|
||||
}
|
||||
|
||||
import RichDuration._
|
||||
|
||||
def effect(freq: Duration)(op: IO[_]) {
|
||||
Akka.system.scheduler.schedule(freq, freq.randomize()) { op.unsafePerformIO }
|
||||
}
|
||||
|
|
|
@ -104,6 +104,4 @@ final class Finisher(
|
|||
_ ← historyRepo.addEntry(blackUser.usernameCanonical, blackElo, game.id)
|
||||
} yield ()
|
||||
} | io()
|
||||
|
||||
private def !!(msg: String) = failure(msg.wrapNel)
|
||||
}
|
||||
|
|
|
@ -129,11 +129,17 @@ final class Hand(
|
|||
action: Pov ⇒ Valid[IO[A]]): IO[Valid[A]] =
|
||||
fromPov(ref) { pov ⇒ action(pov).sequence }
|
||||
|
||||
private def fromPov[A](fullId: String)(op: Pov ⇒ IO[A]): IO[A] =
|
||||
gameRepo pov fullId flatMap op
|
||||
private def fromPov[A](ref: PovRef)(op: Pov ⇒ IO[Valid[A]]): IO[Valid[A]] =
|
||||
fromPov(gameRepo pov ref)(op)
|
||||
|
||||
private def fromPov[A](ref: PovRef)(op: Pov ⇒ IO[A]): IO[A] =
|
||||
gameRepo pov ref flatMap op
|
||||
private def fromPov[A](fullId: String)(op: Pov ⇒ IO[Valid[A]]): IO[Valid[A]] =
|
||||
fromPov(gameRepo pov fullId)(op)
|
||||
|
||||
private def !!(msg: String) = failure(msg.wrapNel)
|
||||
private def fromPov[A](povIO: IO[Option[Pov]])(op: Pov ⇒ IO[Valid[A]]): IO[Valid[A]] =
|
||||
povIO flatMap { povOption ⇒
|
||||
povOption.fold(
|
||||
pov ⇒ op(pov),
|
||||
io { "No such game".failNel }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ final class SystemEnv(config: Config) {
|
|||
makeHistory = gameHistory)
|
||||
|
||||
lazy val gameSocket = new game.Socket(
|
||||
getGame = gameRepo.gameOption,
|
||||
getGame = gameRepo.game,
|
||||
hand = hand,
|
||||
hubMemo = gameHubMemo,
|
||||
messenger = messenger)
|
||||
|
|
|
@ -12,12 +12,14 @@ object AppApiC extends LilaController {
|
|||
|
||||
def show(fullId: String) = Action {
|
||||
Async {
|
||||
(api show fullId).asPromise map JsonIOk
|
||||
(api show fullId).asPromise map { op ⇒
|
||||
op.unsafePerformIO.fold(e ⇒ BadRequest(e.shows), JsonOk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def reloadTable(gameId: String) = Action {
|
||||
IOk(api reloadTable gameId)
|
||||
ValidIOk(api reloadTable gameId)
|
||||
}
|
||||
|
||||
def start(gameId: String) = Action { implicit request ⇒
|
||||
|
|
|
@ -28,12 +28,18 @@ trait LilaController extends Controller with ContentTypes with RequestGetter {
|
|||
_ ⇒ Ok("ok")
|
||||
)
|
||||
|
||||
def FormValidIOk[A](form: Form[A])(op: A ⇒ IO[Unit])(implicit request: Request[_]) =
|
||||
def FormIOk[A](form: Form[A])(op: A ⇒ IO[Unit])(implicit request: Request[_]) =
|
||||
form.bindFromRequest.fold(
|
||||
form ⇒ BadRequest(form.errors mkString "\n"),
|
||||
data ⇒ IOk(op(data))
|
||||
)
|
||||
|
||||
def FormValidIOk[A](form: Form[A])(op: A ⇒ IO[Valid[Unit]])(implicit request: Request[_]) =
|
||||
form.bindFromRequest.fold(
|
||||
form ⇒ BadRequest(form.errors mkString "\n"),
|
||||
data ⇒ ValidIOk(op(data))
|
||||
)
|
||||
|
||||
def IOk(op: IO[Unit]) = Ok(op.unsafePerformIO)
|
||||
|
||||
// I like Unit requests.
|
||||
|
|
|
@ -19,37 +19,30 @@ import scalaz.effects._
|
|||
class GameRepo(collection: MongoCollection)
|
||||
extends SalatDAO[RawDbGame, String](collection) {
|
||||
|
||||
def game(gameId: String): IO[DbGame] = io {
|
||||
if (gameId.size != gameIdSize)
|
||||
throw new Exception("Invalid game id " + gameId)
|
||||
findOneByID(gameId) flatMap decode err "No game found for id " + gameId
|
||||
}
|
||||
|
||||
def gameOption(gameId: String): IO[Option[DbGame]] = io {
|
||||
def game(gameId: String): IO[Option[DbGame]] = io {
|
||||
if (gameId.size != gameIdSize) None
|
||||
else findOneByID(gameId) flatMap decode
|
||||
}
|
||||
|
||||
def pov(ref: PovRef): IO[Pov] = pov(ref.gameId, ref.color)
|
||||
|
||||
def pov(gameId: String, color: Color): IO[Pov] =
|
||||
game(gameId) map { g ⇒ Pov(g, g player color) }
|
||||
|
||||
def player(gameId: String, color: Color): IO[DbPlayer] =
|
||||
game(gameId) map { g ⇒ g player color }
|
||||
|
||||
def pov(fullId: String): IO[Pov] =
|
||||
game(fullId take gameIdSize) map { g ⇒
|
||||
val playerId = fullId drop gameIdSize
|
||||
val player = g player playerId err "No player found for id " + fullId
|
||||
Pov(g, player)
|
||||
def player(gameId: String, color: Color): IO[Option[DbPlayer]] =
|
||||
game(gameId) map { gameOption ⇒
|
||||
gameOption map { _ player color }
|
||||
}
|
||||
|
||||
def povOption(gameId: String, color: Color): IO[Option[Pov]] =
|
||||
gameOption(gameId) map { gOption ⇒
|
||||
gOption map { g ⇒ Pov(g, g player color) }
|
||||
def pov(gameId: String, color: Color): IO[Option[Pov]] =
|
||||
game(gameId) map { gameOption ⇒
|
||||
gameOption map { g ⇒ Pov(g, g player color) }
|
||||
}
|
||||
|
||||
def pov(fullId: String): IO[Option[Pov]] =
|
||||
game(fullId take gameIdSize) map { gameOption ⇒
|
||||
gameOption flatMap { g ⇒
|
||||
g player (fullId drop gameIdSize) map { Pov(g, _) }
|
||||
}
|
||||
}
|
||||
|
||||
def pov(ref: PovRef): IO[Option[Pov]] = pov(ref.gameId, ref.color)
|
||||
|
||||
def save(game: DbGame): IO[Unit] = io {
|
||||
update(DBObject("_id" -> game.id), _grater asDBObject encode(game))
|
||||
}
|
||||
|
|
|
@ -36,6 +36,4 @@ extends CappedRepo[Message](collection, max) {
|
|||
def encode(obj: Message): DBObject = DBObject(
|
||||
"u" -> obj.username,
|
||||
"t" -> obj.text)
|
||||
|
||||
private def !!(msg: String) = failure(msg.wrapNel)
|
||||
}
|
||||
|
|
|
@ -27,21 +27,28 @@ final class Api(
|
|||
entryData: String,
|
||||
messageString: String,
|
||||
hookOwnerId: String,
|
||||
myHookOwnerId: Option[String]): IO[Unit] = for {
|
||||
myHookOwnerId: Option[String]): IO[Valid[Unit]] = for {
|
||||
hook ← hookRepo ownedHook hookOwnerId
|
||||
color ← ioColor(colorName)
|
||||
game ← gameRepo game gameId
|
||||
p1 ← starter.start(game, entryData)
|
||||
p2 ← messenger.systemMessages(game, messageString) map p1.++
|
||||
_ ← gameRepo save p2
|
||||
_ ← gameSocket send p2
|
||||
_ ← hook.fold(h ⇒ fisherman.bite(h, p2.game), io())
|
||||
_ ← myHookOwnerId.fold(
|
||||
ownerId ⇒ hookRepo ownedHook ownerId flatMap { myHook ⇒
|
||||
myHook.fold(fisherman.delete, io())
|
||||
gameOption ← gameRepo game gameId
|
||||
result ← (Color(colorName) |@| gameOption).tupled.fold(
|
||||
colorGame ⇒ {
|
||||
val (color, game) = colorGame
|
||||
for {
|
||||
p1 ← starter.start(game, entryData)
|
||||
p2 ← messenger.systemMessages(game, messageString) map p1.++
|
||||
_ ← gameRepo save p2
|
||||
_ ← gameSocket send p2
|
||||
_ ← hook.fold(h ⇒ fisherman.bite(h, p2.game), io())
|
||||
_ ← myHookOwnerId.fold(
|
||||
ownerId ⇒ hookRepo ownedHook ownerId flatMap { myHook ⇒
|
||||
myHook.fold(fisherman.delete, io())
|
||||
},
|
||||
io())
|
||||
} yield success()
|
||||
},
|
||||
io())
|
||||
} yield ()
|
||||
io(GameNotFound)
|
||||
)
|
||||
} yield result
|
||||
|
||||
def create(hookOwnerId: String): IO[Unit] = for {
|
||||
hook ← hookRepo ownedHook hookOwnerId
|
||||
|
|
|
@ -32,7 +32,7 @@ final class Preload(
|
|||
myHook: Option[Hook],
|
||||
std: () ⇒ IO[Response]): IO[Response] = myHook.fold(
|
||||
h ⇒ h.gameId.fold(
|
||||
ref ⇒ gameRepo gameOption ref map { game ⇒
|
||||
ref ⇒ gameRepo game ref map { game ⇒
|
||||
game.fold(
|
||||
g ⇒ redirect(g fullIdOf g.creatorColor),
|
||||
redirect()
|
||||
|
|
|
@ -31,6 +31,10 @@ package object lila
|
|||
override val typeHintStrategy = StringTypeHintStrategy(when = TypeHintFrequency.Never)
|
||||
}
|
||||
|
||||
def !!(msg: String) = msg.failNel
|
||||
|
||||
val GameNotFound = !!("Game not found")
|
||||
|
||||
implicit def addPP[A](a: A) = new {
|
||||
def pp[A] = a ~ println
|
||||
}
|
||||
|
|
|
@ -46,5 +46,5 @@ final class ReverseEngineering(fromGame: Game, to: Board) {
|
|||
} toList
|
||||
}
|
||||
|
||||
private def !!(msg: String) = failure(msg.wrapNel)
|
||||
private def !!(msg: String) = msg.failNel
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue