Permalink
Browse files

[reactivemongo] Specs (connection handler), add optional code in quer…

…y error
  • Loading branch information...
cchantep
cchantep committed Sep 15, 2014
1 parent 66cc12f commit 1f714141517f5dc547a30482cf50c1ce1f60d3f7
View
@@ -32,61 +32,64 @@ Pattern matching can be used in handler to dispatch result accordingly.
```scala
import reactivemongo.bson.{ BSONInteger, BSONString }
import acolyte.reactivemongo.{ CollectionName, RequestBody, Property, & }
queryRequest match {
case RequestBody("a-mongo-db.a-col-name", _) =>
// Any request on collection "a-mongo-db.a-col-name"
resultA
case RequestBody(colNameOfAnyOther, _) => resultB // Any request
case RequestBody(colName, (k1, v1) :: (k2, v2) :: Nil) =>
// Any request with exactly 2 BSON properties
resultC
case RequestBody("db.col", ("email", BSONString(v)) :: _) =>
// Request on db.col starting with email string property
resultD
case RequestBody("db.col", ("name", BSONString("eman")) :: _) =>
// Request on db.col starting with an "name" string property,
// whose value is "eman"
resultE
case RequestBody(_, ("age": ValueDocument(
("$gt", BSONInteger(minAge)) :: Nil))) =>
// Request on any collection, with an "age" document as property,
// itself with exactly one integer "$gt" property
// e.g. `{ 'age': { '$gt', 10 } }`
resultF
case RequestBody("db.col", ~(Property("email"), BSONString(e))) =>
// Request on db.col with an "email" string property,
// anywhere in properties (possible with others which are ignored there)
resultG
case RequestBody("db.col", ~(Property("name"), BSONString("eman"))) =>
// Request on db.col with an "name" string property with "eman" as value,
// anywhere in properties (possibly with others which are ignored there).
resultH
case RequestBody(colName,
~(Property("age"), BSONInteger(age)) &
~(Property("email"), BSONString(v))) =>
// Request on any collection, with an "age" integer property
// and an "email" string property, possibly not in this order.
resultI
case RequestBody(colName,
~(Property("age"), ValueDocument(
~(Property("$gt"), BSONInteger(minAge)))) &
~(Property("email"), BSONString(email))) =>
// Request on any collection, with an "age" property with itself
// a operator property "$gt" having an integer value, and an "email"
// property (at the same level as age), without order constraint.
resultJ
import acolyte.reactivemongo.{
CollectionName, QueryHandler, RequestBody, Property, &
}
val queryHandler = QueryHandler { queryRequest =>
queryRequest match {
case RequestBody("a-mongo-db.a-col-name", _) =>
// Any request on collection "a-mongo-db.a-col-name"
resultA
case RequestBody(colNameOfAnyOther, _) => resultB // Any request
case RequestBody(colName, (k1, v1) :: (k2, v2) :: Nil) =>
// Any request with exactly 2 BSON properties
resultC
case RequestBody("db.col", ("email", BSONString(v)) :: _) =>
// Request on db.col starting with email string property
resultD
case RequestBody("db.col", ("name", BSONString("eman")) :: _) =>
// Request on db.col starting with an "name" string property,
// whose value is "eman"
resultE
case RequestBody(_, ("age": ValueDocument(
("$gt", BSONInteger(minAge)) :: Nil))) =>
// Request on any collection, with an "age" document as property,
// itself with exactly one integer "$gt" property
// e.g. `{ 'age': { '$gt', 10 } }`
resultF
case RequestBody("db.col", ~(Property("email"), BSONString(e))) =>
// Request on db.col with an "email" string property,
// anywhere in properties (possible with others which are ignored there)
resultG
case RequestBody("db.col", ~(Property("name"), BSONString("eman"))) =>
// Request on db.col with an "name" string property with "eman" as value,
// anywhere in properties (possibly with others which are ignored there).
resultH
case RequestBody(colName,
~(Property("age"), BSONInteger(age)) &
~(Property("email"), BSONString(v))) =>
// Request on any collection, with an "age" integer property
// and an "email" string property, possibly not in this order.
resultI
case RequestBody(colName,
~(Property("age"), ValueDocument(
~(Property("$gt"), BSONInteger(minAge)))) &
~(Property("email"), BSONString(email))) =>
// Request on any collection, with an "age" property with itself
// a operator property "$gt" having an integer value, and an "email"
// property (at the same level as age), without order constraint.
resultJ
}
}
```
@@ -113,12 +116,14 @@ request match {
In case of write operation, handler is given the write operator along with the request itself, so dispatch can be based on this information (and combine with pattern matching on request content).
```scala
import acolyte.reactivemongo.{ DeleteOp, InsertOp, UpdateOp }
(operator, writeRequest) match {
case (DeleteOp, RequestBody("a-mongo-db.a-col-name", _)) => resultDelete
case (InsertOp, _) => resultInsert
case (UpdateOp, _) => resultUpdate
import acolyte.reactivemongo.{ WriteHandler, DeleteOp, InsertOp, UpdateOp }
val handler = WriteHandler { (op, wreq) =>
(op, wreq) match {
case (DeleteOp, RequestBody("a-mongo-db.a-col-name", _)) => resultDelete
case (InsertOp, _) => resultInsert
case (UpdateOp, _) => resultUpdate
}
}
```
@@ -52,9 +52,12 @@ object ConnectionHandler {
* ConnectionHandler(myQueryHandler, myWriteHandler)
* }}}
*/
def apply[A, B](q: A = QueryHandler.empty, h: B = WriteHandler.empty)(implicit f: A QueryHandler, g: B WriteHandler): ConnectionHandler = new ConnectionHandler {
val queryHandler = f(q)
val writeHandler = g(h)
def apply[A, B](queryHandler: A = QueryHandler.empty, writeHandler: B = WriteHandler.empty)(implicit f: A QueryHandler, g: B WriteHandler): ConnectionHandler = {
val q = queryHandler; val w = writeHandler
new ConnectionHandler {
val queryHandler = f(q)
val writeHandler = g(w)
}
}
/**
@@ -90,14 +93,14 @@ object QueryHandler {
*
* }}}
*/
implicit def SimpleQueryHandler(f: Request PreparedResponse): QueryHandler = new QueryHandler {
implicit def apply(f: Request PreparedResponse): QueryHandler = new QueryHandler {
def apply(chanId: Int, q: Request): Option[Try[Response]] = f(q)(chanId)
}
/**
* Empty query handler, not handling any request.
*/
lazy val empty = SimpleQueryHandler(_ QueryResponse(None))
lazy val empty = apply(_ QueryResponse(None))
}
/** Write handler. */
@@ -128,14 +131,15 @@ object WriteHandler {
* val handler1: WriteHandler = // Returns a successful empty response
* (w: (WriteOp, Request)) => WriteResponse(false)
*
* val handler2 = WriteHandler { (op: WriteOp,
* }}}
*/
implicit def SimpleWriteHandler(f: (WriteOp, Request) PreparedResponse): WriteHandler = new WriteHandler {
implicit def apply(f: (WriteOp, Request) PreparedResponse): WriteHandler = new WriteHandler {
def apply(chanId: Int, op: WriteOp, w: Request): Option[Try[Response]] = f(op, w)(chanId)
}
/**
* Empty query handler, not handling any request.
*/
lazy val empty = SimpleWriteHandler((_, _) WriteResponse(None))
lazy val empty = apply((_, _) WriteResponse(None))
}
@@ -6,7 +6,7 @@ import org.jboss.netty.buffer.ChannelBuffers
import scala.util.Try
import reactivemongo.bson.BSONDocument
import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONValue }
import reactivemongo.core.protocol.{
Delete,
Insert,
@@ -27,9 +27,10 @@ object MongoDB {
* @param channelId Unique ID of channel
* @param error Error message
*/
def QueryError(channelId: Int, error: String): Try[Response] =
mkResponse(channelId, 1 /*QueryFailure*/ ,
Seq(BSONDocument("$err" -> error)))
def QueryError(channelId: Int, error: String, code: Option[Int] = None): Try[Response] = mkResponse(channelId, 1 /*QueryFailure*/ , {
val doc = BSONDocument("$err" -> error)
code.fold(Seq(doc)) { c Seq(doc ++ ("code" -> c)) }
})
/**
* Builds a response for a success query.
@@ -15,6 +15,14 @@ object QueryResponse {
/** Named factory for error response. */
def failed(message: String) = apply(message)
/**
* Named factory for error response.
*
* @param message Error message
* @param code Error code
*/
def failed(message: String, code: Int) = apply(message -> code)
/** Factory for successful response. */
def successful(result: BSONDocument*) = apply(result)
@@ -14,7 +14,7 @@ trait QueryResponseMaker[T] extends ((Int, T) ⇒ Option[Try[Response]]) {
* @param channelId ID of Mongo channel
* @param result Optional result to be wrapped into response
*/
override def apply(channelId: Int, result: T): Option[Try[Response]]
def apply(channelId: Int, result: T): Option[Try[Response]]
}
/** Response maker companion. */
@@ -29,10 +29,22 @@ object QueryResponseMaker {
*/
implicit def TraversableQueryResponseMaker[T <: Traversable[BSONDocument]] =
new QueryResponseMaker[T] {
override def apply(channelId: Int, result: T): Option[Try[Response]] =
def apply(channelId: Int, result: T): Option[Try[Response]] =
Some(MongoDB.QuerySuccess(channelId, result))
}
/**
* {{{
* import reactivemongo.bson.BSONDocument
* import acolyte.reactivemongo.QueryResponseMaker
*
* val maker = implicitly[QueryResponseMaker[BSONDocument]]
* }}}
*/
implicit def SingleQueryResponseMaker = new QueryResponseMaker[BSONDocument] {
def apply(channelId: Int, result: BSONDocument): Option[Try[Response]] = Some(MongoDB.QuerySuccess(channelId, Seq(result)))
}
/**
* Provides response maker for an error.
*
@@ -43,10 +55,24 @@ object QueryResponseMaker {
* }}}
*/
implicit def ErrorQueryResponseMaker = new QueryResponseMaker[String] {
override def apply(channelId: Int, error: String): Option[Try[Response]] =
def apply(channelId: Int, error: String): Option[Try[Response]] =
Some(MongoDB.QueryError(channelId, error))
}
/**
* Provides response maker for an error.
*
* {{{
* import acolyte.reactivemongo.QueryResponseMaker
*
* val maker = implicitly[QueryResponseMaker[(String, Int)]]
* }}}
*/
implicit def ErrorCodeQueryResponseMaker = new QueryResponseMaker[(String, Int)] {
def apply(channelId: Int, error: (String, Int)): Option[Try[Response]] =
Some(MongoDB.QueryError(channelId, error._1, Some(error._2)))
}
/**
* Provides response maker for handler not supporting specific query.
*
@@ -59,6 +85,6 @@ object QueryResponseMaker {
*/
implicit def UndefinedQueryResponseMaker = new QueryResponseMaker[None.type] {
/** @return None */
override def apply(channelId: Int, undefined: None.type): Option[Try[Response]] = None
def apply(channelId: Int, undefined: None.type): Option[Try[Response]] = None
}
}
@@ -14,7 +14,7 @@ trait WriteResponseMaker[T] extends ((Int, T) ⇒ Option[Try[Response]]) {
* @param channelId ID of Mongo channel
* @param result Optional result to be wrapped into response
*/
override def apply(channelId: Int, result: T): Option[Try[Response]]
def apply(channelId: Int, result: T): Option[Try[Response]]
}
/** Response maker companion. */
@@ -27,7 +27,7 @@ object WriteResponseMaker {
* }}}
*/
implicit def SuccessWriteResponseMaker = new WriteResponseMaker[Boolean] {
override def apply(channelId: Int, updatedExisting: Boolean): Option[Try[Response]] = Some(MongoDB.WriteSuccess(channelId, updatedExisting))
def apply(channelId: Int, updatedExisting: Boolean): Option[Try[Response]] = Some(MongoDB.WriteSuccess(channelId, updatedExisting))
}
/**
@@ -38,7 +38,7 @@ object WriteResponseMaker {
* }}}
*/
implicit def UnitWriteResponseMaker = new WriteResponseMaker[Unit] {
override def apply(channelId: Int, effect: Unit): Option[Try[Response]] = Some(MongoDB.WriteSuccess(channelId, false))
def apply(channelId: Int, effect: Unit): Option[Try[Response]] = Some(MongoDB.WriteSuccess(channelId, false))
}
/**
@@ -51,7 +51,7 @@ object WriteResponseMaker {
* }}}
*/
implicit def ErrorWriteResponseMaker = new WriteResponseMaker[String] {
override def apply(channelId: Int, error: String): Option[Try[Response]] =
def apply(channelId: Int, error: String): Option[Try[Response]] =
Some(MongoDB.WriteError(channelId, error, None))
}
@@ -66,7 +66,7 @@ object WriteResponseMaker {
*/
implicit def ErrorCodeWriteResponseMaker =
new WriteResponseMaker[(String, Int)] {
override def apply(channelId: Int, error: (String, Int)): Option[Try[Response]] = Some(MongoDB.WriteError(channelId, error._1, Some(error._2)))
def apply(channelId: Int, error: (String, Int)): Option[Try[Response]] = Some(MongoDB.WriteError(channelId, error._1, Some(error._2)))
}
/**
@@ -82,6 +82,6 @@ object WriteResponseMaker {
*/
implicit def UndefinedWriteResponseMaker = new WriteResponseMaker[None.type] {
/** @return None */
override def apply(channelId: Int, undefined: None.type): Option[Try[Response]] = None
def apply(channelId: Int, undefined: None.type): Option[Try[Response]] = None
}
}
Oops, something went wrong.

0 comments on commit 1f71414

Please sign in to comment.