Skip to content
Permalink
Browse files

[reactivemongo] Refactor DSL and better support for Count command

  • Loading branch information
cchantep
cchantep committed Sep 25, 2014
1 parent cb826da commit 646c8f57fccf605d97e2318adebb4b2540d5514b
@@ -10,7 +10,7 @@ object Acolyte extends Build with Dependencies
aggregate(scalacPlugin, reactiveMongo, jdbcDriver, jdbcScala, studio).
settings(
organization in ThisBuild := "org.eu.acolyte",
version in ThisBuild := "1.0.25",
version in ThisBuild := "1.0.26",
javaOptions in ThisBuild ++= Seq("-source", "1.6", "-target", "1.6"),
scalaVersion in ThisBuild := "2.10.4",
crossScalaVersions in ThisBuild := Seq("2.10.4", "2.11.2"),
@@ -32,13 +32,13 @@ val res: Future[String] = withDriver(yourConnectionHandler) { d =>
As in previous example, main API object is [AcolyteDSL](https://github.com/cchantep/acolyte/blob/master/reactive-mongo/src/main/scala/acolyte/reactivemongo/AcolyteDSL.scala).

Dependency can be added to SBT project with `"org.eu.acolyte" %% "reactive-mongo" % "1.0.25"`, and in a Maven one as following:
Dependency can be added to SBT project with `"org.eu.acolyte" %% "reactive-mongo" % "1.0.26"`, and in a Maven one as following:

```xml
<dependency>
<groupId>org.eu.acolyte</groupId>
<artifactId>reactive-mongo</artifactId>
<version>1.0.25</version>
<version>1.0.26</version>
</dependency>
```

@@ -63,14 +63,19 @@ Acolyte provides several ways to initialize Mongo resources (driver, connection,
- `withConnection` and `withFlatConnection`,
- `withDB` and `withFlatDB`,
- `withCollection` and `withFlatCollection`,
- `withQueryResult` and `withFlatQueryResult`.
- `withQueryHandler` and `withFlatQueryHandler`,
- `withQueryResult` and `withFlatQueryResult`,
- `withWriteHandler` and `withFlatWriteHandler`,
- `withWriteResult` and `withFlatWriteResult`.

> Naming convention is `withX(...) { a => b }` to use with your Mongo function which doesn't return `Future` result, and `withFlatX(...) { a => b }` when your Mongo function return result (to flatten `withFlatX` result as `Future[YourReturnType]`, not having for example `Future[Future[YourReturnType]]`).
```scala
import reactivemongo.api.{ MongoConnection, MongoDriver }
import reactivemongo.bson.BSONDocument
import acolyte.reactivemongo.AcolyteDSL
import acolyte.reactivemongo.{
AcolyteDSL, QueryResponse, PreparedResponse, Request, WriteOp
}
// Simple cases
AcolyteDSL.withDriver(yourHandler) { d =>
@@ -89,10 +94,19 @@ AcolyteDSL.withCollection(yourHandler, "colName") { col =>
yourFunctionWorkingWithCol(col)
}
AcolyteDSL.withQueryHandler({ req: Request =>
val resp: PreparedResponse = QueryResponse.empty // empty doc list
resp
}) { d => yourFunctionWorkingWithDriver(d) }
AcolyteDSL.withQueryResult(queryResultForAll) { d =>
yourFunctionWorkingWithDriver(d)
}
AcolyteDSL.withWriteHandler({ cmd: (WriteOp, Request) => aResp }) { d =>
yourFunctionWorkingWithDriver(d)
}
AcolyteDSL.withWriteResult(writeResultForAll) { d =>
yourFunctionWorkingWithDriver(d)
}
@@ -248,6 +262,10 @@ val queryHandler = QueryHandler { queryRequest =>
// a operator property "$gt" having an integer value, and an "email"
// property (at the same level as age), without order constraint.
resultJ
case CountRequest(colName, ("email", "em@il.net") :: Nil) =>
// Matching on count query
resultK
}
}
```
@@ -307,14 +325,17 @@ val success3 = QueryResponse(Seq(
val success4 = QueryResponse.successful(
BSONDocument("name" -> "singleResult"), BSONDocument("price" -> 1.2D))
val countResponse = QueryResponse.count(4)
val success5 = QueryResponse.empty // successful empty response
val success6 = QueryResponse(List.empty[BSONDocument]) // equivalent
val countResponse = QueryResponse.count(4) // response to Mongo Count
```

When a handler supports some query cases, but not other, it can return an undefined response, to let the chance other handlers would manage it.

```scala
val undefined1 = QueryResponse(None)
val undefined2 = QueryResponse.empty
val undefined2 = QueryResponse.undefined
```

### Result creation for write operation
@@ -339,7 +360,7 @@ When a handler supports some write cases, but not other, it can return an undefi

```scala
val undefined1 = WriteResponse(None)
val undefined2 = WriteResponse.empty
val undefined2 = WriteResponse.undefined
```

## Build
@@ -1,10 +1,8 @@
package acolyte.reactivemongo

/**
* Acolyte DSL for ReactiveMongo.
*/
/** Acolyte DSL for ReactiveMongo. */
object AcolyteDSL extends WithDriver
with WithDB with WithCollection with WithResult {
with WithDB with WithCollection with WithHandler with WithResult {

/**
* Creates an empty connection handler.
@@ -34,8 +34,11 @@ object QueryResponse {
def count(result: Int = 0) = apply(BSONDocument("ok" -> 1, "n" -> result))

/**
* Empty/undefined response, returned by handler no supporting
* Undefined response, returned by handler no supporting
* a specific query that may be handled by others.
*/
lazy val empty = apply(None)
lazy val undefined = apply(None)

/** Successful empty response (list of zero document). */
lazy val empty = apply(List.empty[BSONDocument])
}
@@ -2,7 +2,7 @@ package acolyte.reactivemongo

import org.jboss.netty.buffer.ChannelBuffer

import reactivemongo.bson.{ BSONDocument, BSONValue }
import reactivemongo.bson.{ BSONDocument, BSONString, BSONValue }
import reactivemongo.bson.buffer.{
ArrayBSONBuffer,
ReadableBuffer,
@@ -101,14 +101,34 @@ object ValueDocument {
* }
* }}}
*
* @see ValueDocument
* @see [[ValueDocument]]
* @see [[CountRequest]]
*/
object RequestBody {
/**
* @return Collection name -> request body (BSON properties)
*/
def unapply(q: Request): Option[(String, List[(String, BSONValue)])] =
Some(q.collection -> q.body.elements.toList)

}

/**
* Request extractor for Count command.
* @see [[RequestBody]]
*/
object CountRequest {
/**
* @return Collection name -> query body (count BSON properties)
*/
def unapply(q: Request): Option[(String, List[(String, BSONValue)])] =
q match {
case RequestBody(col, ("count", BSONString(_)) ::
("query", ValueDocument(query)) :: Nil) Some(col -> query)
case _ None
}
}

/**
* Meta-extractor, to combine extractor on BSON properties.
* @see RequestBody
@@ -0,0 +1,98 @@
package acolyte.reactivemongo

import scala.concurrent.{ ExecutionContext, Future }

import reactivemongo.api.{ MongoConnection, MongoDriver }

/** Functions to work with handler (provided driver functions). */
trait WithHandler { up: WithDriver

/**
* Works with a Mongo driver handling only queries,
* using given query `handler`.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import reactivemongo.api.MongoDriver
* import acolyte.reactivemongo.{ AcolyteDSL, Request }
*
* AcolyteDSL.withQueryHandler({ req: Request ⇒ aResponse }) { d =>
* val driver: MongoDriver = d
* "aResult"
* }
* }}}
*
* @see [[AcolyteDSL.withDriver]]
* @see [[AcolyteDSL.handleQuery]]
* @see [[AcolyteDSL.withQueryResult]]
*/
def withQueryHandler[T](handler: Request PreparedResponse)(f: MongoDriver T)(implicit m: DriverManager[ConnectionHandler], c: ExecutionContext): Future[T] = withDriver(AcolyteDSL handleQuery QueryHandler(handler))(f)

/**
* Works with a Mongo driver handling only queries,
* using given query `handler`.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import reactivemongo.api.MongoDriver
* import acolyte.reactivemongo.{ AcolyteDSL, Request }
*
* AcolyteDSL.withFlatQueryHandler({ req: Request ⇒ aResponse }) { d =>
* val driver: MongoDriver = d
* Future(1+2)
* }
* }}}
*
* @see [[AcolyteDSL.withFlatDriver]]
* @see [[AcolyteDSL.handleQuery]]
* @see [[AcolyteDSL.withQueryResult]]
*/
def withFlatQueryHandler[T](handler: Request PreparedResponse)(f: MongoDriver Future[T])(implicit m: DriverManager[ConnectionHandler], c: ExecutionContext): Future[T] = withFlatDriver(AcolyteDSL handleQuery QueryHandler(handler))(f)

/**
* Works with a Mongo driver handling only write operations,
* using given write `handler`.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import reactivemongo.api.MongoDriver
* import acolyte.reactivemongo.{ AcolyteDSL, Request, WriteOp }
*
* AcolyteDSL.withWriteHandler({ cmd: (WriteOp, Request) ⇒ aResp }) { d =>
* val driver: MongoDriver = d
* "aResult"
* }
* }}}
*
* @see [[AcolyteDSL.withDriver]]
* @see [[AcolyteDSL.handleWrite]]
* @see [[AcolyteDSL.withWriteResult]
*/
def withWriteHandler[T](handler: (WriteOp, Request) PreparedResponse)(f: MongoDriver T)(implicit m: DriverManager[ConnectionHandler], c: ExecutionContext): Future[T] = withDriver(AcolyteDSL handleWrite handler)(f)

/**
* Works with a Mongo driver handling only write operations,
* using given write `handler`.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import reactivemongo.api.MongoDriver
* import acolyte.reactivemongo.{ AcolyteDSL, Request, WriteOp }
*
* AcolyteDSL.withWriteHandler({ cmd: (WriteOp, Request) ⇒ aResp }) { d =>
* val driver: MongoDriver = d
* Future(1+2)
* }
* }}}
*
* @see [[AcolyteDSL.withFlatDriver]]
* @see [[AcolyteDSL.handleWrite]]
* @see [[AcolyteDSL.withFlatWriteResult]
*/
def withFlatWriteHandler[T](handler: (WriteOp, Request) PreparedResponse)(f: MongoDriver Future[T])(implicit m: DriverManager[ConnectionHandler], c: ExecutionContext): Future[T] = withFlatDriver(AcolyteDSL handleWrite handler)(f)

}
@@ -5,72 +5,54 @@ import scala.concurrent.{ ExecutionContext, Future }
import reactivemongo.api.{ MongoConnection, MongoDriver }

/** Functions to work with result (provided collection functions). */
trait WithResult { up: WithDriver
trait WithResult { withHandler: WithHandler

/**
* Works with a Mongo driver handling only queries,
* and returning given `result` for all of them.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import acolyte.reactivemongo.AcolyteDSL
*
* }}}
* @see [[AcolyteDSL.withDriver]]
* @see [[AcolyteDSL.handleQuery]]
* @see [[AcolyteDSL.withQueryHandler]]
*/
def withQueryResult[A, B](result: A)(f: MongoDriver B)(implicit m: DriverManager[ConnectionHandler], mk: QueryResponseMaker[A], c: ExecutionContext): Future[B] = withDriver(
AcolyteDSL handleQuery { _: Request QueryResponse(result) })(f)
def withQueryResult[A, B](result: A)(f: MongoDriver B)(implicit m: DriverManager[ConnectionHandler], mk: QueryResponseMaker[A], c: ExecutionContext): Future[B] = withQueryHandler({ _: Request QueryResponse(result) })(f)

/**
* Works with a Mongo driver handling only queries,
* and returning given `result` for all of them.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import acolyte.reactivemongo.AcolyteDSL
*
* }}}
* @see [[AcolyteDSL.withFlatDriver]]
* @see [[AcolyteDSL.handleQuery]]
*/
def withFlatQueryResult[A, B](result: A)(f: MongoDriver Future[B])(implicit m: DriverManager[ConnectionHandler], mk: QueryResponseMaker[A], c: ExecutionContext): Future[B] = withFlatDriver(
AcolyteDSL handleQuery { _: Request QueryResponse(result) })(f)
def withFlatQueryResult[A, B](result: A)(f: MongoDriver Future[B])(implicit m: DriverManager[ConnectionHandler], mk: QueryResponseMaker[A], c: ExecutionContext): Future[B] =
withFlatQueryHandler({ _: Request QueryResponse(result) })(f)

/**
* Works with a Mongo driver handling only write operations,
* and returning given `result` for all of them.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import acolyte.reactivemongo.AcolyteDSL
*
* }}}
* @see [[AcolyteDSL.withDriver]]
* @see [[AcolyteDSL.handleWrite]]
* @see [[AcolyteDSL.withWriteHandler]]
*/
def withWriteResult[A, B](result: A)(f: MongoDriver B)(implicit m: DriverManager[ConnectionHandler], mk: WriteResponseMaker[A], c: ExecutionContext): Future[B] = withDriver(AcolyteDSL handleWrite { (_: WriteOp, _: Request)
WriteResponse(result)
})(f)
def withWriteResult[A, B](result: A)(f: MongoDriver B)(implicit m: DriverManager[ConnectionHandler], mk: WriteResponseMaker[A], c: ExecutionContext): Future[B] = withWriteHandler(
{ (_: WriteOp, _: Request) WriteResponse(result) })(f)

/**
* Works with a Mongo driver handling only write operations,
* and returning given `result` for all of them.
* Driver and associated resources are released
* after the function `f` the result `Future` is completed.
*
* {{{
* import acolyte.reactivemongo.AcolyteDSL
*
* }}}
* @see [[AcolyteDSL.withFlatDriver]]
* @see [[AcolyteDSL.handleWrite]]
*/
def withFlatWriteResult[A, B](result: A)(f: MongoDriver Future[B])(implicit m: DriverManager[ConnectionHandler], mk: WriteResponseMaker[A], c: ExecutionContext): Future[B] = withFlatDriver(
AcolyteDSL handleWrite {
(_: WriteOp, _: Request) WriteResponse(result)
})(f)
def withFlatWriteResult[A, B](result: A)(f: MongoDriver Future[B])(implicit m: DriverManager[ConnectionHandler], mk: WriteResponseMaker[A], c: ExecutionContext): Future[B] = withFlatWriteHandler(
{ (_: WriteOp, _: Request) WriteResponse(result) })(f)
}
@@ -37,8 +37,8 @@ object WriteResponse {
apply(count -> updatedExisting)

/**
* Empty/undefined response, returned by handler no supporting
* Undefined response, returned by handler no supporting
* a specific write operation that may be handled by others.
*/
lazy val empty = apply(None)
lazy val undefined = apply(None)
}

0 comments on commit 646c8f5

Please sign in to comment.
You can’t perform that action at this time.