Skip to content

Instantly share code, notes, and snippets.

@nafg
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nafg/838563cc626173777629 to your computer and use it in GitHub Desktop.
Save nafg/838563cc626173777629 to your computer and use it in GitHub Desktop.
One attempt at taming css bound forms without tons of (X => Unit)s
trait Produce { self =>
type F[R]
def runWith(f: =>F[Unit]): CssSel
protected[lrbcol] def combineFunc[R](f: F[R]): (R => Unit) => CssSel
def combine[R](f: F[R]) = new ProduceOne(new Binding(combineFunc(f)))
}
class Binding[A, T](val func: (A => Unit) => T) {
var realHandler: () => A => Unit = null
private val closure: A => Unit = a => realHandler()(a)
val result = func(closure)
}
class ProduceOne[A](val binding: Binding[A, CssSel]) extends Produce {
type F[R] = A => R
def runWith(f: =>(A => Unit)) = {
binding.realHandler = () => f
binding.result
}
protected[lrbcol] def combineFunc[R](f: F[R]): (R => Unit) => CssSel =
g => binding.func(a => g(f(a)))
}
class ProduceMany[A, Next <: Produce](val binding: Binding[A, CssSel], val next: Next) extends Produce {
type F[R] = A => next.F[R]
def runWith(f: =>(A => next.F[Unit])): CssSel = {
var nextFunc: Option[next.F[Unit]] = None
binding.realHandler = { () => a => nextFunc = Some(f(a)) }
val nextCssSel = next.runWith(nextFunc.get)
binding.result & nextCssSel
}
protected[lrbcol] def combineFunc[R](f: F[R]): (R => Unit) => CssSel =
g => binding.func(a => next.combineFunc(f(a))(g))
}
implicit class ProduceOps[P <: Produce](protected val produce: P) {
def &:[A](head: ProduceOne[A]): ProduceMany[A, P] = new ProduceMany[A, P](new Binding(f => head.runWith(f)), produce)
def &:(css: CssSel) = new Produce {
type F[R] = produce.F[R]
def runWith(f: =>F[Unit]) = produce.runWith(f) & css
def combineFunc[R](f: F[R]) = produce.combineFunc[R](f)
}
}
implicit class CssSel_&:(css: CssSel) {
def &:[A](head: ProduceOne[A]): ProduceOne[A] = new ProduceOne[A](head.binding) {
override def runWith(f: =>F[Unit]) = css & super.runWith(f)
}
def &:(css2: CssSel) = css2 & css
}
class Producer[A, T](val func: (A => Unit) => T) {
def andThen[U](g: T => U) =
new Producer[A, U](f => g(func(f)))
}
implicit class flattenableProducer[A, T <: Produce](producer: Producer[A, T]) {
// def flatten
}
class produce[A] {
def apply[T](f: (A => Unit) => T) = new Producer[A, T](f)
}
def produce[A, T](f: (A => Unit) => T) = new Producer[A, T](f)
def produce[A] = new produce[A]
implicit class produceBind[P](pred: P)(implicit conv: P => ToCssBindPromoter) {
def #->[A, T](producer: Producer[A, T])(implicit canBind: CanBind[T]): ProduceOne[A] =
new ProduceOne[A](new Binding[A, CssSel](f => pred #> producer.func(f)))
def #*>[A, P <: Produce](producers: Seq[Producer[A, P]]) = {
val bindings: Seq[Binding[A, P]] = producers.map(p => new Binding[A, P](f => p.func(f)))
val css = pred #> bindings.map(_.result.runWith(???))
val buf = scala.collection.mutable.ListBuffer.empty[A]
val handler = { (f: Seq[A] => Unit) =>
f(buf)
css
}
bindings.foreach { b =>
b.realHandler = { () => a => bindings.synchronized {
buf +=a
}}
}
val seqBinding = new Binding[Seq[A], CssSel](handler)
new ProduceOne[Seq[A]](seqBinding)
}
def #=>[A, P <: Produce](p: P) = {
new ProduceOne[A](p.binding.andThen(pred #> _))
}
def #*>>[A, T](producers: Seq[Producer[A, T]])(implicit canBind: CanBind[Seq[T]]): ProduceOne[Seq[A]] = {
val bindings: Seq[Binding[A, T]] = producers.map(p => new Binding[A, T](f => p.func(f)))
val css = pred #> bindings.map(_.result)
val buf = scala.collection.mutable.ListBuffer.empty[A]
val handler = { (f: Seq[A] => Unit) =>
f(buf)
css
}
bindings.foreach { b =>
b.realHandler = { () => a => bindings.synchronized {
buf +=a
}}
}
val seqBinding = new Binding[Seq[A], CssSel](handler)
new ProduceOne[Seq[A]](seqBinding)
}
}
def text(init: String) = produce[String](SHtml.text(init, _))
def checkbox(init: Boolean) = produce[Boolean](f => (_: NodeSeq) match {
case e: Elem => SHtml.checkbox(init, f, (_: Elem) % e.attributes)
case _ => SHtml.checkbox(init, f)
})
def select[A](values: Seq[(A, String)], default: Option[A]) = produce[A](f => SHtml.selectObj[A](values, default, f))
def ifOrEmpty[A](cond: Boolean)(as: =>Seq[A]) = if(cond) as else Nil
def render = {
implicit val pathPrefix = PathPrefix.default
val resetPin = ("#resetpin" #> onSubmit{
def reset() = vol.runSafe(vol.pin.reset())
val existingPins = withSession { implicit session => SlickVolunteers.map(_.pin).list }
do reset()
while (existingPins.contains(vol.pin.get))
})
val volForm1 = (
"#code" #-> text(vol.code.get) &:
"#first" #-> text(vol.first.get) &:
"#last" #-> text(vol.last.get) &:
"#tel" #-> text(vol.phone.get) &:
"#cell" #-> text(vol.cell.get) &:
"#email" #-> text(vol.email.get) &:
"#sms" #-> checkbox(vol.sms.get) &:
"#address" #-> text(vol.address.get) &:
"#city" #-> text(vol.city.get) &:
"#state" #-> text(vol.state.get) &:
"#zip" #-> text(vol.zip.get) &:
"#bloodType" #-> select(BloodTypes.values.toList.map(v => (v, v.toString)), Some(vol.bloodType.get)) &:
"#cmvNegative" #-> checkbox(vol.cmvNegative.get) &:
"#dateJoined" #-> text(vol.dateJoined.toString) &:
"#active" #-> checkbox(vol.active.get) &:
"#pin" #> vol.pin.toString
)
val volForm = volForm1.runWith {
code => first => last => phone => cell => email => sms => address => city => state => zip =>
bloodType => cmvNegative => dateJoined => active =>
vol.code.set(code)
vol.first.set(first)
vol.last.set(last)
vol.phone.set(phone)
vol.cell.set(cell)
vol.email.set(email)
vol.sms.set(sms)
vol.address.set(address)
vol.city.set(city)
vol.state.set(state)
vol.zip.set(zip)
vol.bloodType.set(bloodType)
vol.cmvNegative.set(cmvNegative)
vol.dateJoined.setFromAny(dateJoined)
vol.active.set(active)
}
val tagsForm = "#tags" #-> (if(Login.currentUser map (_.hasAnyRole(Role.Administrator, Role.Coordinator)) openOr false) {
val hasHospTag = for {
h <- SlickHospitals
t = vol.hospitalsIDTagFor.filter(SlickHospitals.lookup(h) === SlickHospitals.lookup(_)).exists
} yield (h, t)
val producers: Seq[Producer[Boolean, Produce{ type F[R] = Boolean => R }]] = withSession { implicit session => hasHospTag.list }.map{ case (hosp, hasTag) =>
val id = "hosp" + hosp.id.toString
produce[Boolean]{ f =>
"label [for]" #> id &:
".name *" #> hosp.name.get &:
(".hastag" #-> checkbox(hasTag).andThen(f => (ns: NodeSeq) => f(ns) match { case e: Elem => e % new xml.UnprefixedAttribute("id", id, xml.Null) }))
}
}
val x = ".eachHospital" #*> producers
x
} else { ??? })
volForm &
resetPin &
"#tags" #> Util.IfOrClear(Login.currentUser map (_.hasAnyRole(Role.Administrator, Role.Coordinator)) openOr false){
val hasHospTag = for {
h <- SlickHospitals
t = vol.hospitalsIDTagFor.filter(SlickHospitals.lookup(h) === SlickHospitals.lookup(_)).exists
} yield (h, t)
".eachHospital" #> withSession { implicit session => hasHospTag.list }.map{ case (hosp, hasTag) =>
val id = "hosp" + hosp.id.toString
"label [for]" #> id &
".name *" #> hosp.name.get &
".hastag" #> SHtml.checkbox(
hasTag,
_ match {
case true if !hasTag =>
withSession { implicit session =>
VolunteerHospitalIDTags.map(vhit => (vhit.volunteer, vhit.hospital)).insert((Some(SlickVolunteers.Lookup(vol.id.get)), Some(SlickHospitals.Lookup(hosp.id.get))))
}
case false if hasTag =>
withSession { implicit session =>
val q = for {
vhit <- VolunteerHospitalIDTags
if vhit.hospital === SlickHospitals.Lookup(hosp.id.get)
if vhit.volunteer === SlickVolunteers.Lookup(vol.id.get)
} yield vhit
q.delete
}
case _ =>
},
"id" -> id
)
}
} &
@nafg
Copy link
Author

nafg commented May 20, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment