diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..58572c9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# reactiveneo [![Build Status](https://travis-ci.org/websudos/reactiveneo.svg?branch=develop)](https://travis-ci.org/websudos/reactiveneo) + +Reactive type-safe Scala DSL for Neo4j + +Contributing to ReactiveNeo +============================================== +Back to top + +Contributions are most welcome! + +1. If you don't have direct write access(e.g. you are not from Websudos), fork the repository first. +2. Create a feature branch where all the changes will be made. +3. Do your awesome stuff! +4. Create a release branch according to the GitFlow guidelines. +5. Create a Pull Request from that release branch. +6. Wait for us to merge!(P.S.: We like to think we're really quick at that) + + +Using GitFlow +================================== +Back to top + +To contribute, simply submit a "Pull request" via GitHub. + +We use GitFlow as a branching model and SemVer for versioning. + +- When you submit a "Pull request" we require all changes to be squashed. +- We never merge more than one commit at a time. All the n commits on your feature branch must be squashed. +- We won't look at the pull request until Travis CI says the tests pass, make sure tests go well. + +Scala Style Guidelines +=================================================== +Back to top + +In spirit, we follow the [Twitter Scala Style Guidelines](http://twitter.github.io/effectivescala/). +We will reject your pull request if it doesn't meet code standards, but we'll happily give you a hand to get it right. Morpheus is even using ScalaStyle to +build, which means your build will also fail if your code doesn't comply with the style rules. + +Some of the things that will make us seriously frown: + +- Blocking when you don't have to. It just makes our eyes hurt when we see useless blocking. +- Testing should be thread safe and fully async, use ```ParallelTestExecution``` if you want to show off. +- Writing tests should use the pre-existing tools. +- Use the common patterns you already see here, we've done a lot of work to make it easy. +- Don't randomly import stuff. We are very big on alphabetized clean imports. +- Morpheus uses ScalaStyle during Travis CI runs to guarantee you are complying with our guidelines. Since breaking the rules will result in a failed build, +please take the time to read through the guidelines beforehand. + + diff --git a/README.md b/README.md index dbfcf96..6a3f431 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@ -# reactiveneo +# reactiveneo [![Build Status](https://travis-ci.org/websudos/reactiveneo.svg?branch=develop)](https://travis-ci.org/websudos/reactiveneo) + +Reactive type-safe Scala DSL for Neo4j + + +# Table of contents + +
    +
  1. Graph modelling
  2. +
  3. Nodes
  4. +
  5. Relationships
  6. +
  7. Indexes
  8. +
  9. Querying
  10. +
-Reactive typesafe Scala DSL for Neo4j The library enforces strong type checks that imposes some restrictions on query format. Every node and relationship used in the query needs to be defined and named. @@ -15,9 +27,13 @@ MATCH (wallstreet:Movie { title:'Wall Street' })<-[r:ACTED_IN]-(actor:Actor) RETURN r ``` + + # Graph modelling +Back to top ## Nodes +Back to top Domain model class ``` @@ -42,22 +58,74 @@ class PersonNode extends Node[PersonNode, Person] { ``` ## Relationships +Back to top +Reactiveneo relationship definition +``` +import com.websudos.reactiveneo.dsl._ + +class PersonRelation extends Relationship[PersonRelation, Person] { + + object name extends StringAttribute with Index + + object age extends IntegerAttribute + + def fromNode(data: QueryRecord): Person = { + Person(name[String](data), age[Int](data)) + } + +} +``` ## Indexes +Back to top # Querying +Back to top In this example all nodes of Person type are returned. ``` -scala> val personNodes = matches[Person].return(p => p).execute +scala> val personNodes = Person.returns(p => p).execute personNodes: Future[Seq[Person]] ``` You can also query for specific attributes of a node. ``` -scala> val personNames = matches[Person].return(p => p.name).execute +scala> val personNames = Person.returns(p => p.name).execute personNames: Future[Seq[String]] ``` + +A query that involves attributes matching. +``` +scala> val personNodes = Person( p => p.name := "Tom" ).returns(p => p).execute +personNodes: Future[Seq[Person]] +``` + +Query for a person that has a relationship to another person +``` +scala> val personNodes = Person.relatedTo[Person].returns(p => p).execute +personNodes: Future[Seq[Person]] +``` + +Query for a person that has a relationship to another person with given name +``` +scala> val personNodes = Person.relatedTo(Person(p => p.name := "James").returns(p => p).execute +personNodes: Future[Seq[Person]] +``` + + +Query for a person that has a relationship to another person +``` +scala> val personNodes = Person.relatedTo(WorkRelationship :-> Person).returns((p1,r,p2) => p1).execute +personNodes: Future[Seq[Person]] +``` + + +Query for a person that has a relationship to another person with given name +``` +scala> val personNodes = Person.relatedTo(WorkRelationship(r => r.company := "ABC") :-> Person(p => p.name := "John")) + returns((p1,r,p2) => p1).execute +personNodes: Future[Seq[Person]] +``` diff --git a/project/Build.scala b/project/Build.scala index 937d93f..1314beb 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -21,11 +21,11 @@ import org.scalastyle.sbt.ScalastylePlugin object reactiveneo extends Build { - val newzlyUtilVersion = "0.1.19" - val scalatestVersion = "2.2.0-M1" - val finagleVersion = "6.17.0" + val UtilVersion = "0.4.0" + val ScalatestVersion = "2.2.0-M1" + val FinagleVersion = "6.20.0" val playVersion = "2.3.4" - val scalazVersion = "7.0.6" + val ScalazVersion = "7.1.0" val publishUrl = "http://maven.websudos.co.uk" @@ -115,9 +115,9 @@ object reactiveneo extends Build { "com.chuusai" % "shapeless_2.10.4" % "2.0.0", "com.github.nscala-time" %% "nscala-time" % "1.0.0", "com.typesafe.scala-logging" %% "scala-logging-slf4j" % "2.1.2", - "com.newzly" %% "util-testing" % newzlyUtilVersion % "test", - "org.scalaz" %% "scalaz-scalacheck-binding" % scalazVersion % "test", - "org.scalatest" %% "scalatest" % scalatestVersion % "test, provided", + "com.websudos" %% "util-testing" % UtilVersion % "test", + "org.scalaz" %% "scalaz-scalacheck-binding" % ScalazVersion % "test", + "org.scalatest" %% "scalatest" % ScalatestVersion % "test, provided", "org.scalamock" %% "scalamock-scalatest-support" % "3.0.1" % "test" ), fork in Test := true, @@ -147,8 +147,8 @@ object reactiveneo extends Build { libraryDependencies ++= Seq( "com.chuusai" % "shapeless_2.10.4" % "2.0.0", "org.scala-lang" % "scala-reflect" % "2.10.4", - "com.twitter" %% "finagle-http" % finagleVersion, - "com.twitter" %% "util-core" % finagleVersion, + "com.twitter" %% "finagle-http" % FinagleVersion, + "com.twitter" %% "util-core" % FinagleVersion, "joda-time" % "joda-time" % "2.3", "org.joda" % "joda-convert" % "1.6", "com.typesafe.play" %% "play-json" % playVersion, @@ -165,8 +165,8 @@ object reactiveneo extends Build { ).settings( name := "reactiveneo-zookeeper", libraryDependencies ++= Seq( - "com.twitter" %% "finagle-serversets" % finagleVersion, - "com.twitter" %% "finagle-zookeeper" % finagleVersion + "com.twitter" %% "finagle-serversets" % FinagleVersion, + "com.twitter" %% "finagle-zookeeper" % FinagleVersion ) ) @@ -177,13 +177,13 @@ object reactiveneo extends Build { ).settings( name := "reactiveneo-testing", libraryDependencies ++= Seq( - "com.twitter" %% "util-core" % finagleVersion, - "com.newzly" %% "util-testing" % newzlyUtilVersion, - "org.scalatest" %% "scalatest" % scalatestVersion, + "com.twitter" %% "util-core" % FinagleVersion, + "com.websudos" %% "util-testing" % UtilVersion, + "org.scalatest" %% "scalatest" % ScalatestVersion, "org.scalacheck" %% "scalacheck" % "1.11.3", "org.fluttercode.datafactory" % "datafactory" % "0.8", - "com.twitter" %% "finagle-http" % finagleVersion, - "com.twitter" %% "util-core" % finagleVersion + "com.twitter" %% "finagle-http" % FinagleVersion, + "com.twitter" %% "util-core" % FinagleVersion ) ).dependsOn( reactiveneoZookeeper diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/attribute/AbstractAttribute.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/attribute/AbstractAttribute.scala index 5fe3c0f..c3c82ba 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/attribute/AbstractAttribute.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/attribute/AbstractAttribute.scala @@ -62,4 +62,4 @@ class IntegerAttribute[Owner <: GraphObject[Owner, R], R](graphObject: GraphObje query[Int](name).get } -} \ No newline at end of file +} diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/JsonParser.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/JsonParser.scala index 21cec40..8b82b93 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/JsonParser.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/JsonParser.scala @@ -45,12 +45,12 @@ abstract class JsonParser[R] extends ResultParser[R] { private[this] def singleErrorMessage(error: (JsPath, scala.Seq[ValidationError])) = { val (path: JsPath, errors: Seq[ValidationError]) = error - val message = errors.foldLeft(errors.head.message)((acc,err) => s"$acc,${err.message}") + val message = errors.foldLeft(errors.head.message)((acc, err) => s"$acc,${err.message}") s"Errors at $path: $message" } private[client] def buildErrorMessage(error: JsError) = { - error.errors.tail.foldLeft(singleErrorMessage(error.errors.head))((acc,err) => s"acc,${singleErrorMessage(err)}") + error.errors.tail.foldLeft(singleErrorMessage(error.errors.head))((acc, err) => s"acc,${singleErrorMessage(err)}") } override def parseResult(response: HttpResponse): Try[R] = { @@ -69,4 +69,4 @@ abstract class JsonParser[R] extends ResultParser[R] { */ class JsonValidationException(msg: String) extends Exception -class InvalidResponseException(msg: String) extends Exception \ No newline at end of file +class InvalidResponseException(msg: String) extends Exception diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/ServerCall.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/ServerCall.scala index 3f60410..142a312 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/ServerCall.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/client/ServerCall.scala @@ -17,6 +17,8 @@ package com.websudos.reactiveneo.client import com.websudos.reactiveneo.dsl.ReturnExpression import org.jboss.netty.handler.codec.http.HttpMethod +import scala.concurrent.Future + /** * REST API endpoints definitions. * @param path Server query path. @@ -42,7 +44,7 @@ class ServerCall[RT](endpoint: RestEndpoint, content: Option[String], returnExpr } - def execute = { + def execute: Future[Seq[RT]] = { val result = client.makeRequest[Seq[RT]](endpoint.path, endpoint.method) result } @@ -58,4 +60,4 @@ object ServerCall { def apply[RT](endpoint: RestEndpoint, returnExpression: ReturnExpression[RT])(implicit client: RestClient) = { new ServerCall[RT](endpoint, None, returnExpression) } -} \ No newline at end of file +} diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/GraphObject.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/GraphObject.scala index 8f807db..e091d73 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/GraphObject.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/GraphObject.scala @@ -62,6 +62,15 @@ private[reactiveneo] abstract class GraphObject[Owner <: GraphObject[Owner, Reco */ def attributes: List[AbstractAttribute[_]] = _attributes.toList + /** + * Constructs a pattern for the class related to this accompanying object. + */ + def apply(predBuilder: (Owner => Predicate[_])*) + (implicit m: Manifest[Owner]): Pattern[Owner] = { + val obj = m.runtimeClass.newInstance().asInstanceOf[Owner] + val pattern = Pattern(obj, nodeAliases.head, predBuilder.map(pred => pred(obj)): _*) + pattern + } Lock.synchronized { diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQuery.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQuery.scala index fd19630..179798d 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQuery.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQuery.scala @@ -15,7 +15,7 @@ package com.websudos.reactiveneo.dsl import com.websudos.reactiveneo.client.{ServerCall, SingleTransaction, RestClient} -import com.websudos.reactiveneo.query.{BuiltQuery, CypherKeywords, CypherQueryBuilder} +import com.websudos.reactiveneo.query.{CypherOperators, BuiltQuery, CypherKeywords, CypherQueryBuilder} import scala.annotation.implicitNotFound import scala.concurrent.Future @@ -79,20 +79,27 @@ private[reactiveneo] class MatchQuery[ def query: String = builtQuery.queryString + @implicitNotFound("You cannot use two where clauses on a single query") + final def relatesTo(go: GraphObject[GO, _])(implicit ev: WB =:= WhereUnbound): MatchQuery[GO, WB, RB, OB, LB, _] = { + new MatchQuery[GO, WB, RB, OB, LB, Any] ( + node, + builtQuery, + aliases) + } + @implicitNotFound("You cannot use two where clauses on a single query") final def where(condition: GO => Criteria[GO])(implicit ev: WB =:= WhereUnbound): MatchQuery[GO, WhereBound, RB, OB, LB, _] = { new MatchQuery[GO, WhereBound, RB, OB, LB, Any] ( node, - where(builtQuery, condition(node).clause), + builtQuery.appendSpaced(CypherKeywords.RETURN).appendSpaced(CypherOperators.WILDCARD), aliases) } - final def returns[URT](ret: GO => ReturnExpression[URT]): MatchQuery[GO, WB, ReturnBound, OB, LB, URT] = { + final def returns[URT](ret: GO => ReturnExpression[URT]): MatchQuery[GO, WB, ReturnBound, OB, LB, URT] = { new MatchQuery[GO, WB, ReturnBound, OB, LB, URT] ( node, - builtQuery.appendSpaced(CypherKeywords.RETURN).appendSpaced(ret(node).query(aliases)), - aliases, - Some(ret(node))) + builtQuery.appendSpaced(CypherKeywords.RETURN).appendSpaced(aliases.values.mkString(",")), + aliases) } @implicitNotFound("You need to add return clause to capture the type of result") diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQueryImplicits.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQueryImplicits.scala index 1dd3931..15fa6b3 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQueryImplicits.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/MatchQueryImplicits.scala @@ -35,4 +35,18 @@ trait MatchQueryImplicits { MatchQuery.createRootQuery(pattern, nodeAliases.tail) } + /** + * Conversion that simplifies query building. It allows to build the query directly from a pattern. + * ``` + * PersonNode(p=>p.name := "Mark").returns(p=>p) + * ``` + * @param p Predicate that forms initial node for the query + * @tparam N Type of start node. + * @return Returns query object. + */ + implicit def patternToQuery[N <: Node[N, _]](p: Pattern[N]): MatchQuery[N, WhereUnbound, ReturnUnbound, OrderUnbound, LimitUnbound, _] = { + MatchQuery.createRootQuery(p, nodeAliases.tail) + } + + } diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Pattern.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Pattern.scala index e5587bc..bffe0a8 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Pattern.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Pattern.scala @@ -49,7 +49,7 @@ private[reactiveneo] case class Predicate[T]( attribute: AbstractAttribute[T], value: T)(implicit formatter: ValueFormatter[T]) { val clause: BuiltQuery = { - if(value == null) + if (value == null) throw new IllegalArgumentException("NULL is not allowed value to be used in predicate.") new BuiltQuery(attribute.name).append(CypherOperators.COLON).append(value) } @@ -64,4 +64,4 @@ object Predicate { } } -} \ No newline at end of file +} diff --git a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Relationship.scala b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Relationship.scala index f6694d0..b20dcac 100644 --- a/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Relationship.scala +++ b/reactiveneo-dsl/src/main/scala/com/websudos/reactiveneo/dsl/Relationship.scala @@ -19,7 +19,8 @@ package com.websudos.reactiveneo.dsl * * User needs to extend this class when defining nodes he/she wants to use in the queries. */ -abstract class Relationship[Owner <: Relationship[Owner, Record], Record] extends GraphObject[Owner, Record] { +abstract class Relationship[Owner <: Relationship[Owner, Record], Record] + extends GraphObject[Owner, Record] { } diff --git a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/CypherResultParserTest.scala b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/CypherResultParserTest.scala index 6bf5be9..7992992 100644 --- a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/CypherResultParserTest.scala +++ b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/CypherResultParserTest.scala @@ -30,10 +30,10 @@ class CypherResultParserTest extends FlatSpec with Matchers { | "columns":["a"], | "data":[ | { - | "row":["test1"] + | "row":[{"name":"test1"}] | }, | { - | "row":["test2"] + | "row":[{"name":"test2"}] | } | ] | }], diff --git a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/RestClientTest.scala b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/RestClientTest.scala index 7b34e55..1312297 100644 --- a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/RestClientTest.scala +++ b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/RestClientTest.scala @@ -15,16 +15,15 @@ package com.websudos.reactiveneo.client import java.net.InetSocketAddress -import java.nio.charset.Charset import java.util.concurrent.TimeUnit -import com.newzly.util.testing.AsyncAssertionsHelper._ import com.twitter.finagle.Service import com.twitter.finagle.builder.{Server, ServerBuilder} import com.twitter.finagle.http.Http import com.twitter.io.Charsets.Utf8 import com.twitter.util.Future import com.websudos.reactiveneo.client.RestClient._ +import com.websudos.util.testing._ import org.jboss.netty.buffer.ChannelBuffers.copiedBuffer import org.jboss.netty.handler.codec.http.HttpResponseStatus._ import org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1 @@ -33,9 +32,9 @@ import org.scalatest._ import org.scalatest.concurrent.PatienceConfiguration import org.scalatest.time.SpanSugar._ +import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.FiniteDuration import scala.util.Try -import scala.concurrent.ExecutionContext.Implicits.global class RestClientTest extends FlatSpec with Matchers with BeforeAndAfter { @@ -63,7 +62,7 @@ class RestClientTest extends FlatSpec with Matchers with BeforeAndAfter { val result = client.makeRequest("/") result.successful { res => res.getStatus.getCode should equal(200) - res.getContent.toString(Charset.forName("UTF-8")) should equal("neo") + res.getContent.toString(Utf8) should equal("neo") } } @@ -72,7 +71,7 @@ class RestClientTest extends FlatSpec with Matchers with BeforeAndAfter { val client = new RestClient(ClientConfiguration("localhost", 6666, FiniteDuration(10, TimeUnit.SECONDS))) implicit val parser = new ResultParser[String] { override def parseResult(response: HttpResponse): Try[String] = { - Try(response.getContent.toString(Charset.forName("UTF-8"))) + Try(response.getContent.toString(Utf8)) } } val result: scala.concurrent.Future[String] = client.makeRequest("/") diff --git a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/ServerCallTest.scala b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/ServerCallTest.scala index 8563cae..ccb4a0a 100644 --- a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/ServerCallTest.scala +++ b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/client/ServerCallTest.scala @@ -16,9 +16,9 @@ package com.websudos.reactiveneo.client import com.websudos.reactiveneo.dsl.{ObjectReturnExpression, TestNode, TestNodeRecord} import org.scalatest.{Matchers, FlatSpec} -import com.newzly.util.testing.AsyncAssertionsHelper._ import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global +import com.websudos.util.testing._ class ServerCallTest extends FlatSpec with Matchers with ServerMockSugar { diff --git a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/MatchQueryTest.scala b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/MatchQueryTest.scala index 351c240..4e6d7be 100644 --- a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/MatchQueryTest.scala +++ b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/MatchQueryTest.scala @@ -20,18 +20,23 @@ import Predicate._ class MatchQueryTest extends FlatSpec with Matchers with ReturnImplicits { it should "build a simple query with a predicate" in { - matches[TestNode]( node => node.name := "Tom" ).returns(go => go).query shouldEqual "MATCH (a:TestNode {name:'Tom'}) RETURN a " + TestNode( node => node.name := "Tom" ).returns(go => go).query shouldEqual "MATCH (a:TestNode {name:'Tom'}) RETURN a " } + it should "build a simple query with a predicate using conversion" in { + TestNode( node => node.name := "Tom" ).returns(go => go).query shouldEqual "MATCH (a:TestNode {name:'Tom'}) RETURN a " + } + + it should "build a simple query without any predicate" in { - matches[TestNode]().returns(n => n).query shouldEqual "MATCH (a:TestNode) RETURN a " + TestNode().returns(n => n).query shouldEqual "MATCH (a:TestNode) RETURN a " } it should "build a query returning single object" in { - matches[TestNode]().returns(n => n).query shouldEqual "MATCH (a:TestNode) RETURN a " + TestNode().returns(n => n).query shouldEqual "MATCH (a:TestNode) RETURN a " } it should "build a query returning object attribute" in { - matches[TestNode]().returns(_.name).query shouldEqual "MATCH (a:TestNode) RETURN a.name " + TestNode().returns(_.name).query shouldEqual "MATCH (a:TestNode) RETURN a.name " } -} \ No newline at end of file +} diff --git a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/TestNodeRecord.scala b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/TestNodeRecord.scala index 309c5b8..9c47fce 100644 --- a/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/TestNodeRecord.scala +++ b/reactiveneo-dsl/src/test/scala/com/websudos/reactiveneo/dsl/TestNodeRecord.scala @@ -27,4 +27,6 @@ class TestNode extends Node[TestNode, TestNodeRecord] { override def fromQuery(data: QueryRecord): TestNodeRecord = { TestNodeRecord(name(data)) } -} \ No newline at end of file +} + +object TestNode extends TestNode diff --git a/reactiveneo-testing/src/main/scala/com/websudos/reactiveneo/client/ServerMock.scala b/reactiveneo-testing/src/main/scala/com/websudos/reactiveneo/client/ServerMock.scala index a9ac5a7..8f1fdda 100644 --- a/reactiveneo-testing/src/main/scala/com/websudos/reactiveneo/client/ServerMock.scala +++ b/reactiveneo-testing/src/main/scala/com/websudos/reactiveneo/client/ServerMock.scala @@ -16,12 +16,6 @@ package com.websudos.reactiveneo.client import java.net.InetSocketAddress -import com.newzly.util.testing.AsyncAssertionsHelper._ -import com.twitter.finagle.Service -import com.twitter.finagle.builder.{ServerBuilder, Server} -import com.twitter.finagle.http.Http -import com.twitter.io.Charsets._ -import com.twitter.util.Future import org.jboss.netty.buffer.ChannelBuffers._ import org.jboss.netty.handler.codec.http.HttpResponseStatus._ import org.jboss.netty.handler.codec.http.HttpVersion._ @@ -29,6 +23,14 @@ import org.jboss.netty.handler.codec.http.{DefaultHttpResponse, HttpRequest, Htt import org.scalatest.concurrent.PatienceConfiguration import org.scalatest.time.SpanSugar._ +import com.twitter.finagle.Service +import com.twitter.finagle.builder.{ServerBuilder, Server} +import com.twitter.finagle.http.Http +import com.twitter.io.Charsets._ +import com.twitter.util.Future + +import com.websudos.util.testing._ + /** * A simple server to be used in testing. Server is occupying a socket and therefore should be closed after the test. */ @@ -60,7 +62,7 @@ class ServerMock(handler: (HttpRequest) => HttpResponse) { trait ServerMockSugar { - implicit def fromString(contents: String) = { + implicit def fromString(contents: String): DefaultHttpResponse = { val response = new DefaultHttpResponse(HTTP_1_1, OK) response.setContent(copiedBuffer(contents, Utf8)) response @@ -71,4 +73,4 @@ trait ServerMockSugar { block(server.server.localAddress.asInstanceOf[InetSocketAddress]) } -} \ No newline at end of file +}