Eine typisierte Filter-API
makeProperty macht aus einem Selektor einen benannten Property-Griff. Das reicht für eine kleine Abfrage-API: Der Compiler prüft die Property, der Griff benennt sie für die Anzeige, und der Accessor liest sie.
API
where(_.year)("> 1950")(_ > 1950)
Ein Filter hält den Property-Griff, eine lesbare Beschreibung und den Test. inline reicht den Selektor an das Makro durch.
scala
final case class Filter[T, V](property: PropertyWithAccessor[T, V], operator: String, test: V => Boolean) {
def apply(value: T): Boolean = test(property.get(value))
def describe: String = s"${property.name} $operator"
}
inline def where[T, V](inline selector: T => V)(operator: String)(test: V => Boolean): Filter[T, V] =
Filter(PropertySupport.makeProperty[T, V](selector), operator, test)
Live
Eine Liste filtern
Wähle einen Filter. Die Beschriftung kommt aus dem Property-Griff, nicht aus einem String, den du zweimal tippst.
Bücher
Drei Filter über einer Liste.
- filter.describe
- year > 1950
- books.filter(filter).map(_.title)
- The Left Hand of Darkness, Foundation, The Dispossessed
Über Listen hinaus
Derselbe Griff, andere Ziele
property.name braucht eine Datenbankabfrage, ein URL-Parameter oder ein JSON-Patch-Pfad. Der Test läuft im Speicher; der Name reist.
scala
def toQueryParameter(filter: Filter[?, ?], value: String): String =
s"${filter.property.name}=${java.net.URLEncoder.encode(value, "UTF-8")}"
json-mapper arbeitet genauso
EntitySchema.property(_.title) ist genau dieses Muster: ein Selektor hinein, ein benannter Property-Griff mit Accessor heraus.