Last active
July 2, 2016 10:49
-
-
Save guersam/2c72fa36977e494ea1db to your computer and use it in GitHub Desktop.
Generate a bidimensional table from a sequence of arbitrary case class
This file contains hidden or 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 cats.Show | |
import shapeless._ | |
import shapeless.ops.record._ | |
import shapeless.ops.hlist._ | |
import scala.collection.immutable | |
trait Tablifier[A] { | |
def head: List[String] | |
def rows(objs: immutable.Seq[A]): List[List[String]] | |
} | |
object Tablifier extends TablifierDerivation { | |
implicit def apply[A](implicit t: Tablifier[A]): Tablifier[A] = t | |
} | |
trait TablifierDerivation { | |
implicit def derive[ | |
A, | |
GenRepr <: HList, | |
KeysRepr <: HList, | |
ValuesRepr <: HList, | |
StrRepr <: HList | |
] ( | |
implicit | |
gen: LabelledGeneric.Aux[A, GenRepr], | |
keys: Keys.Aux[GenRepr, KeysRepr], | |
values: Values.Aux[GenRepr, ValuesRepr], | |
keysMapper: Mapper.Aux[toColumnTitle.type, KeysRepr, StrRepr], | |
valuesMapper: Mapper.Aux[toColumnValue.type, ValuesRepr, StrRepr], | |
toStrList: ToList[StrRepr, String] | |
): Tablifier[A] = | |
new Tablifier[A] { | |
override val head: List[String] = keys.apply.map(toColumnTitle).toList | |
override def rows(objs: immutable.Seq[A]): List[List[String]] = | |
objs | |
.iterator | |
.map { obj => | |
val repr = gen.to(obj) | |
values.apply(repr).map(toColumnValue).toList | |
} | |
.toList | |
} | |
} | |
object toColumnTitle extends Poly1 { | |
implicit def keyToName[A] = at[Symbol with A](_.name) | |
} | |
object toColumnValue extends Poly1 with ToColumnValue0 { | |
implicit def booleanCase = at[Boolean](bool => if (bool) "O" else "X") | |
implicit def bigDecimalCase = at[BigDecimal](x => x formatted (if (x.isValidLong) "%.0f" else "%.4f")) | |
implicit def fractionalCase[A: Fractional] = at[A](_ formatted "%.4f") | |
implicit def optionCase[A](implicit c: Case.Aux[A, String]) = at[Option[A]](_.fold("-")(c(_))) | |
} | |
trait ToColumnValue1 { this: Poly1 => | |
implicit def default[A] = at[A](_.toString) | |
} | |
trait ToColumnValue0 extends ToColumnValue1 { this: Poly1 => | |
implicit def showCase[A](implicit s: Show[A]) = at[A](s.show) | |
} |
This file contains hidden or 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
object TablifierDemo extends App { | |
sealed trait Perk | |
object Perk { | |
case object Polymorph extends Perk | |
implicit val perkShow: Show[Perk] = | |
new Show[Perk] { | |
override def show(p: Perk): String = s"[$p]" | |
} | |
} | |
case class Row(id: Int, name: String, isHuman: Boolean, weight: BigDecimal, perk: Option[Perk]) | |
val rows = List( | |
Row(1, "Finn", true, BigDecimal("12.34567"), None), | |
Row(2, "Jake", false, BigDecimal("1234567"), Some(Perk.Polymorph)) | |
) | |
val tablifier = Tablifier[Row] | |
assert(tablifier.head == List("id", "name", "isHuman", "weight", "perk")) | |
assert( | |
tablifier.rows(rows) == List( | |
List("1", "Finn", "O", "12.3457", "-"), | |
List("2", "Jake", "X", "1234567", "[Polymorph]") | |
) | |
) | |
} |
This file contains hidden or 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
@genTable[A](title: String, level: Int, objs: immutable.Seq[A], drop: Int = 0)(implicit tablifier: Tablifier[A]) = { | |
@if(objs.nonEmpty) { | |
<h@level>@title</h@level> | |
<table class="table table-bordered table-hover"> | |
<thead> | |
<tr> | |
@for(key <- tablifier.head.drop(drop)) { | |
<th>@key</th> | |
} | |
</tr> | |
</thead> | |
<tbody> | |
@for(row <- tablifier.rows(objs)) { | |
<tr> | |
@for(col <- row.drop(drop)) { | |
<td>@col</td> | |
} | |
</tr> | |
} | |
</tbody> | |
</table> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment