Skip to content

Instantly share code, notes, and snippets.

@kryptt
Last active August 29, 2015 14:20
Show Gist options
  • Save kryptt/95ecd3c9afd729a31151 to your computer and use it in GitHub Desktop.
Save kryptt/95ecd3c9afd729a31151 to your computer and use it in GitHub Desktop.
Idea of adding a State Monad around PreparedStatementIO
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