Skip to content

Instantly share code, notes, and snippets.

@amazari
Created July 15, 2015 12:14
Show Gist options
  • Save amazari/d8d99510e44c9feeaf7c to your computer and use it in GitHub Desktop.
Save amazari/d8d99510e44c9feeaf7c to your computer and use it in GitHub Desktop.
import com.github.nscala_time.time.StaticInterval
import com.twitter.algebird.Monoid
import shapeless._, record._, syntax.singleton._
import shapeless.ops.record.SelectAll
/** Represents a path component generator, basically a function keysTemplate.type -> Set[Seq[String]]
*
*/
trait PathPattern {
val keysTemplate: HList
val w: Witness
def apply[L <: HList](keys: keysTemplate.type)
(implicit sel: ops.record.Selector[keysTemplate.type , w.T]): Set[Seq[String]]
}
/** A path component generator taking a Joda Time Interval and generating a list of path components,
* one for each hour contained in the interval
*/
case object HourlyIntervalPathPattern extends PathPattern {
override val keysTemplate: HList = ("interval" ->> StaticInterval.lastHour) :: HNil
override val w = Witness("interval")
override def apply(keys: keysTemplate.type)
(implicit sel: ops.record.Selector[keysTemplate.type , w.T]): Set[Seq[String]] =
_("interval").by[Set](1.hour).map(date =>
Seq(date.toString("YYYY-MM-dd"), date.toString("HH")))
}
case class ConstantStringPathPattern(constant: String) extends PathPattern {
override val keysTemplate: HList = (constant ->> "") :: HNil
override def apply(keys: keysTemplate.type)
(implicit sel: ops.record.Selector[keysTemplate.type , w.T]): Set[Seq[String]] =
Set(Seq(keys(constant)))
}
/** Make the path generators composable
* The resulting generator takes a Shapeless records containing the keys of each sub-generators
*/
implicit object PathPatternMonoid extends Monoid[PathPattern] {
override def zero: PathPattern = new PathPattern {
override val keysTemplate = HNil
override def apply(keys: keysTemplate.type)
(implicit sel: ops.record.Selector[keysTemplate.type , w.T])= Set(Seq(""))
}
override def plus(l: PathPattern, r: PathPattern): PathPattern = new PathPattern {
override val keysTemplate = l.keysTemplate :: r.keysTemplate
override def genPath (pKeys: keysTemplate.type)
(implicit sel: ops.record.Selector[keysTemplate.type , w.T])= for {
p1 <- l(SelectAll[keysTemplate.type, l.keysTemplate.type](pKeys))
p2 <- r(SelectAll[keysTemplate.type, r.keysTemplate.type](pKeys))
} yield (p1 ++ p2)
}
}
/** "Nice" syntax
*
* @param pathPattern
*/
implicit class SlashablePathPattern(pathPattern: PathPattern) {
def /(child: PathPattern)(implicit mon: Monoid[PathPattern]) = mon.plus(pathPattern, child)
}
val RegionHourlyPathPattern = ConstantStringPathPattern("region") / HourlyIntervalPathPattern
val paths = RegionHourlyPathPattern(("region" ->> "US") :: ("interval" ->> StaticInterval.lastHour) :: HNil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment