Last active
August 29, 2015 14:20
-
-
Save kryptt/95ecd3c9afd729a31151 to your computer and use it in GitHub Desktop.
Idea of adding a State Monad around PreparedStatementIO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import doobie.imports.{Composite, HPS, PreparedStatementIO => IO} | |
import generic.isEmpty | |
import scalaz.{Monoid, State} | |
import scalaz.syntax.monad._ | |
import shapeless._ | |
import shapeless.record._ | |
import shapeless.ops.hlist._ | |
import shapeless.ops.record._ | |
object sql { | |
val iosZero: IOS = (().point[IO], 1) | |
type IOS = (IO[Unit], Int) | |
type IOState = State[IOS, IO[Unit]] | |
trait LowerPrioritySetPoly { self: Poly1 => | |
implicit def default[T](implicit C: Composite[T]): self.Case.Aux[T, IOState] = self.at[T] { (t: T) => | |
State { case (ps, i) => | |
val st = ps *> HPS.set(i, t) | |
((st, i + C.length), st) | |
} | |
} | |
def noop[X]: self.Case.Aux[X, IOState] = at[X] {_ => State.gets(_._1)} | |
} | |
object set extends Poly1 with LowerPrioritySetPoly { | |
implicit def hnil: Case.Aux[HNil, IOState] = noop[HNil] | |
implicit def cnil: Case.Aux[CNil, IOState] = noop[CNil] | |
implicit def option[T](implicit C: Composite[T]): Case.Aux[Option[T], IOState] = at[Option[T]] { | |
case None => State.gets(_._1) | |
case Some(t) => default(C)(t) | |
} | |
implicit def list[T](implicit CSE: Case.Aux[T, IOState]): Case.Aux[List[T], IOState] = at[List[T]] { | |
case Nil => State.gets(_._1) | |
case l => for { | |
_ <- CSE(l.head) | |
io <- list(CSE)(l.tail) | |
} yield io | |
} | |
implicit def hlist[H, T <: HList] | |
(implicit CSEH: Case.Aux[H, IOState], CSET: Case.Aux[T, IOState]) | |
: Case.Aux[H :: T, IOState] = at[H :: T] { case h :: t => for { | |
_ <- CSEH(h) | |
io <- CSET(t) | |
} yield io | |
} | |
implicit def hcons[H, T <: Coproduct] | |
(implicit CSEH: Case.Aux[H, IOState], CSET: Case.Aux[T, IOState]) | |
: Case.Aux[H :+: T, IOState] = at[H :+: T] { | |
case Inl(h) => CSEH(h) | |
case Inr(t) => CSET(t) | |
} | |
implicit def generic[G, L <: HList] | |
(implicit GEN: Generic.Aux[G, L], CSE: Case.Aux[L, IOState]) | |
: Case.Aux[G, IOState] = at[G] { g => CSE.apply(GEN.to(g)) } | |
} | |
} | |
object exampleUsage { | |
def listQuery(states: List[String], filter: Option[String], s: Option[SortInfo], p: PageInfo): Process[ConnectionIO, String] = { | |
val fs = filter.cata(_.split(" ").toList, Nil) | |
val params = | |
(if (states.isEmpty) Nil else List( | |
states.map(_ => "?::core.user_state").mkString("u.state IN (", ", ", ")"))) ++ | |
fs.map(_ => "(u.email || u.firstName || u.lastName) ILIKE ?") | |
val where = if (params.isEmpty) "" else params.mkString("WHERE (", ") AND (", ")") | |
val cq = s"SELECT count(*) FROM core.users u $where" | |
val sq = s"""SELECT u.email | |
FROM core.users u LEFT JOIN core.resource_roles(u.id) ur ON true | |
LEFT JOIN core.roles r ON ur.role_id = r.id | |
$where | |
ORDER BY u.${s.fold("id")(_.by)} ${s.fold("ASC")(_.order)} ${s.fold("")(_ => ", u.id")} | |
OFFSET ? | |
LIMIT ?""" | |
(for { | |
_ <- sql.set(states) | |
cps <- sql.set(fs.map(f => s"%$f%")) | |
sps <- sql.set(p) | |
} yield HC.process[String](sq, sps) | |
).eval(sql.iosZero) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment