Created
August 2, 2016 09:11
-
-
Save lemonxah/81175234655c85bec93367a4dfb684b7 to your computer and use it in GitHub Desktop.
This file contains 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
trait ModelWithId[A] { def copy(id: Option[String]):A } | |
trait Model { def deleted: Boolean; def id: Option[String] } | |
trait DAO[A <: Model, B, C, Q] { | |
def interpreter(q: Query, empty: Q): Throwable \/ Q | |
def insert(a: A)(implicit m: Mappable[A, B, C], ev: A ⇒ ModelWithId[A]): A | |
def insertRaw(b: B) | |
def list(skip: Int = 0, limit: Int = 0)(implicit m: Mappable[A, B, C]): Vector[A] | |
def filter(query: Query, skip: Int = 0, limit: Int = 0)(implicit m: Mappable[A, B, C]): Vector[A] | |
def headOption(query: Query)(implicit m: Mappable[A, B, C]): Option[A] | |
def listB[β](skip: Int = 0, limit: Int = 0, fields: Option[List[String]] = None)(implicit m: Mappable[β, B, C]): Vector[β] | |
def filterB[β](query: Query, skip: Int = 0, limit: Int = 0, fields: Option[List[String]] = None)(implicit m: Mappable[β, B, C]): Vector[β] | |
def headOptionB[β](query: Query, fields: Option[List[String]] = None)(implicit m: Mappable[β, B, C]): Option[β] | |
def update(a: A, query: Query)(implicit m: Mappable[A, B, C]) | |
def delete(a: A)(implicit m: Mappable[A, B, C]) | |
def delete(query: Query) | |
def drop(): Unit | |
// SHORTHAND NOTATION | |
def -=(a: A)(implicit m: Mappable[A, B, C]): Unit = delete(a)(m) | |
def +=(a: A)(implicit m: Mappable[A, B, C], ev: A ⇒ ModelWithId[A]): A = insert(a)(m, ev) | |
//def :=(a: A, query: Query)(implicit m: Mappable[A, B, C]) = update(a, query)(m) | |
} | |
trait Writer[A, B] { def write(a: A): B } | |
trait Cleaner[A, B] { def clean(a: A): B } | |
trait Value[+A] { def get: A } | |
case class UnaryValue[+A](a: A) extends Value[A] { override def get: A = a } | |
case class BinaryValue[+A](a1: A, a2: A) extends Value[(A, A)] { override def get: (A, A) = (a1, a2) } | |
case class ListValue[+A](a: List[A]) extends Value[List[A]] { override def get: List[A] = a } | |
case class Field[A](name: String) { | |
def asc: OrderField = OrderField(this, 1) | |
def dsc: OrderField = OrderField(this, -1) | |
def ===(value: A): Query = Equals(this, UnaryValue(value)) | |
def !==(value: A): Query = NotEquals(this, UnaryValue(value)) | |
def <(value: A): Query = LessThan(this, UnaryValue(value)) | |
def >(value: A): Query = GreaterThan(this, UnaryValue(value)) | |
def >=(value: A): Query = GreaterOrEqual(this, UnaryValue(value)) | |
def <=(value: A): Query = LessOrEqual(this, UnaryValue(value)) | |
def =!=(value: List[A]): Query = In(this, ListValue(value)) | |
def =!!=(value: A): Query = ElemMatchList(this, UnaryValue(value)) | |
def <=>(value1: A, value2: A): Query = Between(this, BinaryValue(value1, value2)) | |
} | |
object FieldExtentions { | |
implicit class stringField(field: Field[String]) { | |
def <%>(value: String): Query = Like(field, UnaryValue(value)) | |
def <*>(value: String): Query = Regex(field, UnaryValue(value)) | |
} | |
} | |
sealed trait Query { self ⇒ | |
def &&(t: Query): Query = And(self, t) | |
def ||(t: Query): Query = Or(self, t) | |
def unary_! : Query = Not(self) | |
def orderBy(orderings: List[OrderField]): OrderBy = OrderBy(Some(self), orderings) | |
} | |
case class OrderField(field: Field[_], order: Int) | |
sealed trait UnaryOperator extends Query { def query: Query } | |
sealed trait Operator extends Query { def left: Query; def right: Query } | |
case class Or(left: Query, right: Query) extends Operator | |
case class And(left: Query, right: Query) extends Operator | |
case class Not(query: Query) extends UnaryOperator | |
case class OrderBy(left: Option[Query], field: List[OrderField]) extends Query | |
sealed trait Operand extends Query { def field: Field[_]; def value: Value[_] } | |
case class GreaterOrEqual[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class GreaterThan[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class LessOrEqual[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class LessThan[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class Equals[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class NotEquals[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class In[A](field: Field[A], value: ListValue[A]) extends Operand | |
case class ElemMatchList[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class Like[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class Regex[A](field: Field[A], value: UnaryValue[A]) extends Operand | |
case class Between[A](field: Field[A], value: BinaryValue[A]) extends Operand | |
object QueryExtentions { | |
implicit def itemlist[T](t: T): List[T] = List(t) | |
implicit def tuple2list[T](t: (T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple3list[T](t: (T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple4list[T](t: (T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple5list[T](t: (T, T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple6list[T](t: (T, T, T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple7list[T](t: (T, T, T, T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple8list[T](t: (T, T, T, T, T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple9list[T](t: (T, T, T, T, T, T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
implicit def tuple10list[T](t: (T, T, T, T, T, T, T, T, T, T)): List[T] = t.productIterator.map(_.asInstanceOf[T]).toList | |
} |
This file contains 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
case class MongoQuery(query: DBObject, order: DBObject = MongoDBObject.empty) | |
object MongoInterpreter { | |
// Interpreter typeclasses | |
def opmap: PartialFunction[Operand, String] = { | |
case _: Equals[_] ⇒ "$eq" | |
case _: GreaterThan[_] ⇒ "$gt" | |
case _: GreaterOrEqual[_] ⇒ "$gte" | |
case _: LessThan[_] ⇒ "$lt" | |
case _: LessOrEqual[_] ⇒ "$lte" | |
case _: NotEquals[_] ⇒ "$ne" | |
case _: In[_] ⇒ "$in" | |
case _: Regex[_] ⇒ "$regex" | |
} | |
implicit val MongoOperandWriter = new Writer[Operand, MongoQuery] { | |
override def write(o: Operand): MongoQuery = MongoQuery(o match { | |
case Equals(f, v) ⇒ new BasicDBObject(f.name, v.get) | |
case Between(f, v) ⇒ new BasicDBObject(f.name, new BasicDBObject("$gte", v.get._1).append("$lte", v.get._2)) | |
case ElemMatchList(f, v) ⇒ new BasicDBObject(f.name, new BasicDBObject("$elemMatch", new BasicDBObject("$in", v.get))) | |
case Like(f, v) ⇒ new BasicDBObject(f.name, new BasicDBObject("$regex", s".*${v.get}.*")) | |
case op: Operand ⇒ new BasicDBObject(op.field.name, new BasicDBObject(opmap(op), op.value.get)) | |
}) | |
} | |
implicit val MongoUnaryOperatorWriter = new Writer[UnaryOperator, MongoQuery] { | |
override def write(u: UnaryOperator): MongoQuery = | |
MongoQuery(new BasicDBObject("$not", MongoQueryWriter.write(u.query))) | |
} | |
implicit val MongoOperatorWriter = new Writer[Operator, MongoQuery] { | |
override def write(o: Operator): MongoQuery = MongoQuery((o match | |
{ case _: Or ⇒ $or; case _: And ⇒ $and})(MongoQueryWriter.write(o.left).query,MongoQueryWriter.write(o.right).query)) | |
} | |
implicit val MongoOrderByWriter = new Writer[OrderBy, MongoQuery] { | |
override def write(order: OrderBy): MongoQuery = | |
MongoQuery(order.left.fold(new MongoDBObject())(q ⇒ MongoQueryWriter.write(q).query), | |
order.field.foldLeft(new BasicDBObject()) { case (a, o) ⇒ a.append(o.field.name, o.order) }) | |
} | |
implicit val MongoQueryWriter: Writer[Query, MongoQuery] = new Writer[Query, MongoQuery] { | |
override def write(a: Query): MongoQuery = a match { | |
case op: Operand ⇒ MongoOperandWriter.write(op) | |
case op: Operator ⇒ MongoOperatorWriter.write(op) | |
case op: UnaryOperator ⇒ MongoUnaryOperatorWriter.write(op) | |
case op: OrderBy ⇒ MongoOrderByWriter.write(op) | |
} | |
} | |
} | |
class MongoDAO[A <: Model](coll: MongoCollection) extends DAO[A, DBObject, DBObject, MongoQuery] { | |
import MongoInterpreter._ | |
val logger = LoggerFactory.getLogger(classOf[MongoDAO[A]]) | |
def interpreter(q: Query, empty: MongoQuery = MongoQuery(MongoDBObject.empty)): Throwable \/ MongoQuery = \/.fromTryCatchNonFatal { | |
val qq = implicitly[Writer[Query, MongoQuery]].write(q) | |
logger.trace(s"${Console.BLUE}Query to be run${Console.WHITE}:${Console.GREEN} $qq${Console.RESET}") | |
} | |
override def list(skip: Int = 0, limit: Int = 0)(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
coll.find().skip(skip).limit(limit).toVector.map(m.fromDBType) | |
} | |
override def filter(query: Query, skip: Int = 0, limit: Int = 0)(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
interpreter(query).fold(_ ⇒ Vector[A](), {q ⇒ coll.find(q.query).sort(q.order).skip(skip).limit(limit).toVector.map(m.fromDBType) }) | |
} | |
override def headOption(query: Query)(implicit m: Mappable[A, DBObject, DBObject]): Option[A] = { | |
interpreter(query).fold(_ ⇒ None, q ⇒ coll.find(q.query).sort(q.order).toVector.map(m.fromDBType).headOption) | |
} | |
override def insert(a: A)(implicit m: Mappable[A, DBObject, DBObject], ev: A ⇒ ModelWithId[A]): A = { | |
val aa = a.copy(id = a.id.orElse(UUID.randomUUID().toString.some)) | |
coll.insert(m.toDBType(aa)) | |
aa | |
} | |
override def insertRaw(dbo: DBObject): Unit = { | |
coll.insert(dbo) | |
} | |
override def update(a: A, query: Query)(implicit m: Mappable[A, DBObject, DBObject]) { | |
interpreter(query).map(q ⇒ | |
coll.update(q.query, m.toDBType(a)) | |
) | |
} | |
override def delete(a: A)(implicit m: Mappable[A, DBObject, DBObject]) { | |
coll.findAndRemove(m.toDBType(a)) | |
} | |
override def delete(query: Query): Unit = { | |
interpreter(query).map(q ⇒ coll.findAndRemove(q.query)) | |
} | |
override def drop(): Unit = { | |
coll.drop() | |
} | |
def aggregate[ζ <: AnyRef](fs: DBObject *)(implicit manifest: Manifest[ζ]): Vector[ζ] = { | |
coll.aggregate(fs.toList, AggregationOptions(AggregationOptions.CURSOR)).map { r => | |
grater[ζ].asObject(r) | |
}.toVector | |
} | |
override def listB[β](skip: Int = 0, limit: Int = 0, fields: Option[List[String]] = None)(implicit m: Mappable[β, DBObject, DBObject]): Vector[β] = | |
coll.find(MongoDBObject.empty, fields.map(_.foldLeft(new BasicDBObject())(_.append(_, 1))) | |
.getOrElse(MongoDBObject.empty)).skip(skip).limit(limit).toVector.map(m.fromDBType) | |
override def filterB[β](query: Query, skip: Int = 0, limit: Int = 0, fields: Option[List[String]] = None) | |
(implicit m: Mappable[β, DBObject, DBObject]): Vector[β] = | |
interpreter(query).fold(_ ⇒ Vector[β](), { q ⇒ | |
coll.find(q.query, fields.map(_.foldLeft(new BasicDBObject())(_.append(_, 1))).getOrElse(MongoDBObject.empty)) | |
.sort(q.order).skip(skip).limit(limit).toVector.map(m.fromDBType) | |
}) | |
override def headOptionB[β](query: Query, fields: Option[List[String]] = None)(implicit m: Mappable[β, DBObject, DBObject]): Option[β] = | |
interpreter(query).fold(_ ⇒ None, q ⇒ coll.find(q.query, | |
fields.map(_.foldLeft(new BasicDBObject())(_.append(_, 1))) | |
.getOrElse(MongoDBObject.empty)).sort(q.order).toVector.map(m.fromDBType).headOption) | |
} |
This file contains 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
//Patch request | |
case Success(Patch(fp: FingPatch, user)) ⇒ | |
fp.uuid.fold( | |
UnaryResponse(none[Fing]).toResponse(Status.BAD_REQUEST, "uuid not specified") | |
) { fid ⇒ | |
fing.headOption(EFing.uuid === fid && EFing.deleted === false).fold( | |
UnaryResponse(none[Fing]).toResponse(Status.NOT_FOUND, "Fing not found") | |
) { ef ⇒ | |
def update_name(name: Option[String]): State[EFing, Unit] = State { s ⇒ (name.fold(s)(un ⇒ s.copy(name = un)), ()) } | |
def update_description(description: Option[String]): State[EFing, Unit] = State { s ⇒ (description.fold(s)(un ⇒ s.copy(description = un)), ()) } | |
def update_properties(properties: Option[List[KeyValuePair]]): State[EFing, Unit] = State { s ⇒ (properties.fold(s)(un ⇒ s.copy(properties = un)), ()) } | |
def update_documentation(documentation: Option[Option[String]]): State[EFing, Unit] = State { s ⇒ (documentation.fold(s)(un ⇒ s.copy(documentation = un)), ()) } | |
def update_fingSerialNumber(fingSerialNumber: Option[String]): State[EFing, Unit] = State { s ⇒ (fingSerialNumber.fold(s)(un ⇒ s.copy(fingSerialNumber = un)), ()) } | |
def update_product(product: Option[Option[String]]): State[EFing, Unit] = State { s ⇒ (product.fold(s)(un ⇒ s.copy(product = un)), ()) } | |
def update_categoryType(categoryType: Option[List[String]]): State[EFing, Unit] = State { s ⇒ (categoryType.fold(s)(un ⇒ s.copy(categoryType = un)), ()) } | |
def update_location(location: Option[String]): State[EFing, Unit] = State { s ⇒ (location.fold(s)(un ⇒ s.copy(location = un)), ()) } | |
def update_type(`type`: Option[String]): State[EFing, Unit] = State { s ⇒ (`type`.fold(s)(un ⇒ s.copy(`type` = un)), ()) } | |
def update_subType(subType: Option[Option[String]]): State[EFing, Unit] = State { s ⇒ (subType.fold(s)(un ⇒ s.copy(subType = un)), ()) } | |
def update_model(model: Option[Option[String]]): State[EFing, Unit] = State { s ⇒ (model.fold(s)(un ⇒ s.copy(model = un)), ()) } | |
def updateFing(f: FingPatch): State[EFing, EFing] = for { | |
_ ← update_name(f.name) | |
_ ← update_description(f.description) | |
_ ← update_properties(f.properties) | |
_ ← update_documentation(f.documentation) | |
_ ← update_fingSerialNumber(f.fingSerialNumber) | |
_ ← update_product(f.product) | |
_ ← update_categoryType(f.categoryType) | |
_ ← update_location(f.location) | |
_ ← update_type(f.`type`) | |
_ ← update_subType(f.subType) | |
_ ← update_model(f.model) | |
s ← State.get | |
} yield s | |
val uef = updateFing(fp).run(ef)._2 | |
fing.update(uef, EFing.uuid === fid) | |
UnaryResponse(uef |> fromModel |> some).toResponse(Status.OK, "Fing updated") | |
} | |
}(uuid).publish(Topics.Fing.patchResponse) |
This file contains 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
case class State[S, +A](run: S ⇒ (S, A)) { | |
def map[B](f: A ⇒ B): State[S, B] = flatMap(a ⇒ State.unit(f(a))) | |
def flatMap[B](f: A ⇒ State[S, B]): State[S, B] = State { s ⇒ | |
val (s1, a) = run(s) | |
f(a).run(s1) | |
} | |
} | |
object State { | |
def unit[S, A](a: A): State[S, A] = State {s ⇒ (s, a)} | |
def map2[S, A, B, C](sa: State[S, A], sb: State[S, B])(f: (A, B) ⇒ C): State[S, C] = sa.flatMap(a ⇒ sb.map(b ⇒ f(a,b))) | |
def map3[S, A, B, C, D](sa: State[S, A], sb: State[S, B], sc: State[S,C])(f: (A, B, C) ⇒ D): State[S, D] = | |
sa.flatMap(a ⇒ sb.flatMap(b ⇒ sc.map(c ⇒ f(a,b,c)))) | |
def sequence[S, A](ls: List[State[S, A]]): State[S, List[A]] = ls.foldRight(unit[S,List[A]](List[A]()))((f,acc) ⇒ map2(f, acc)(_ :: _)) | |
def get[S]: State[S,S] = State { s ⇒ (s,s) } | |
def gets[S, A](f: S ⇒ A): State[S, A] = State { s: S ⇒ (s, f(s))} | |
def put[S](s: S): State[S, Unit] = State { _ ⇒ (s, ())} | |
def modify[S](f: S ⇒ S): State[S, Unit] = State { s ⇒ (f(s), ())} | |
def state[S, A](a:A): State[S, A] = State { s ⇒ (s, a)} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment