Skip to content

Instantly share code, notes, and snippets.

@aaabramov
Created February 11, 2018 13:16
Show Gist options
  • Save aaabramov/3fa29477662b44bd1617d937535255dd to your computer and use it in GitHub Desktop.
Save aaabramov/3fa29477662b44bd1617d937535255dd to your computer and use it in GitHub Desktop.
Attempt to create Anti Fraud DSL based on rules
package example.dsl
import scala.language.implicitConversions
case class Card(number: String, firstName: String, lastName: String)
case class User(username: String, email: String)
case class AntiFraudInput(card: Card, user: User, amount: Double)
object AntiFraudInput {
type InputField[T] = AntiFraudInput => T
}
sealed trait FieldOp[T] {
def matches(source: T, arg: T): Boolean
}
object FieldOpSyntax {
def equalTo[T]: ===[T] = new ===[T]()
def greaterThan[T: Ordering]: >[T] = >[T]()
def lessThan[T: Ordering]: <[T] = <[T]()
def greaterThanOrEqualTo[T: Ordering]: >=[T] = >=[T]()
def lessThanOrEqualTo[T: Ordering]: >=[T] = >=[T]()
def contains[T: SelfTypedContainer]: Contains[T] = Contains[T]()
}
case class ContainerWithValue[A, E](holder: A, container: Container[A, E]) {
def contains(elem: E): Boolean = container.contains(holder)(elem)
}
trait Container[A, E] {
def checkThat(value: A): ContainerWithValue[A, E] = ContainerWithValue(value, this)
def contains(elem: A): E => Boolean
}
trait SelfTypedContainer[T] extends Container[T, T] {
override def checkThat(value: T): ContainerWithValue[T, T] = ContainerWithValue(value, this)
override def contains(elem: T): T => Boolean
}
object SelfTypedContainers {
implicit object StringTypedContainer extends SelfTypedContainer[String] {
override def contains(elem: String): String => Boolean = _.contains(elem)
}
}
case class Contains[T: SelfTypedContainer]() extends FieldOp[T] {
def matches(source: T, arg: T): Boolean = implicitly[SelfTypedContainer[T]] checkThat source contains arg
}
case class StringContainer(s: String) extends Container[String, String] {
override def contains(elem: String): String => Boolean = _.contains(elem)
}
object CommonContainers {
implicit def stringContainer(s: String): StringContainer = StringContainer(s)
}
case class >[T: Ordering]() extends FieldOp[T] {
def matches(source: T, arg: T): Boolean = implicitly[Ordering[T]].gt(source, arg)
}
case class >=[T: Ordering]() extends FieldOp[T] {
def matches(source: T, arg: T): Boolean = implicitly[Ordering[T]].gteq(source, arg)
}
case class <[T: Ordering]() extends FieldOp[T] {
def matches(source: T, arg: T): Boolean = implicitly[Ordering[T]].lt(source, arg)
}
case class <=[T: Ordering]() extends FieldOp[T] {
def matches(source: T, arg: T): Boolean = implicitly[Ordering[T]].lteq(source, arg)
}
case class ===[T]() extends FieldOp[T] {
def matches(source: T, arg: T): Boolean = source == arg
}
case class Rule[T](
field: AntiFraudInput.InputField[T],
op: FieldOp[T],
arg: T,
score: Double
) {
def score(af: AntiFraudInput): Double = if (op.matches(field(af), arg)) score else 0
}
object DslTest extends App {
implicit val afInput = AntiFraudInput(
Card("4242 4242 4242 4242", "TEST", "USER"),
User("af_test", "test@gmail.com"), 200
)
import FieldOpSyntax._
import SelfTypedContainers._
val rules = Seq(
Rule(_.amount, greaterThanOrEqualTo, 100.0, 15),
Rule(_.user.email, equalTo, "test@gmail.com", 30),
Rule(_.user.username, contains, "test", 30)
)
val res = rules.foldLeft(0.0)(_ + _.score(afInput))
println(res)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment