Skip to content

Step #8 : integrating Future for Reactive API

mandubian edited this page Nov 8, 2012 · 2 revisions
class DatomicDemoSpec extends Specification {
  "Datomic" should {
    "create simple schema and provision data" in {
      import Datomic._
      import DatomicData._
      import scala.concurrent.ExecutionContext.Implicits.global

      implicit val uri = "datomic:mem://datomicschemaspec"

      //DatomicBootstrap(uri)
      println("created DB with uri %s: %s".format(uri, createDatabase(uri)))

      val person = new Namespace("person") {
        val character = Namespace("person.character")
      }

      val clever = AddIdent( Keyword(person.character, "clever") )
      val violent = AddIdent( person.character / "violent")
      val weak = AddIdent( KW(":person.character/weak") )

      // erase leading ':' to see the error
      val dumb = AddIdent( KW(":person.character/dumb") )

      /* Programmatic creation of a Schema */
      val schema = Seq(
        Attribute( KW(":person/name"), SchemaType.string, Cardinality.one ).withDoc("Person's name"),
        Attribute( KW(":person/age"), SchemaType.long, Cardinality.one ).withDoc("Person's age"),
        Attribute( KW(":person/character"), SchemaType.ref, Cardinality.many ).withDoc("Person's characters"),
        violent,
        weak,
        clever,
        dumb
      )

      /* reactive flow :
       *  - schema creation, 
       *  - provisioning of data
       *  - query 
       */
      val fut = connection.transact(schema).flatMap{ tx => 
        println("Provisioned schema... TX:%s".format(tx))

        /* AddEntity different syntaxes from most programmatic to macrocompiled using inline variables 
         * POTENTIAL DEMO :
         *  - remove a ] from addEntity to show compiling error
         */
        connection.transact(
          AddEntity(DId(Partition.USER))(
            person / "name" -> DString("toto"),
            person / "age" -> DLong(30L),
            person / "character" -> DSeq(weak.ident, dumb.ident)
          ),
          addEntity(DId(Partition.USER))(
            KW(":person/name") -> "tata",
            KW(":person/age") -> 54L,
            KW(":person/character") -> Seq(violent, clever)
          ),
          addEntity("""{
            :db/id ${DId(Partition.USER)}
            :person/name "tutu"
            :person/age 35
            :person/character [ $weak $dumb ]
          }""")
        ).flatMap{ tx => 
          println("Provisioned data... TX:%s".format(tx))

          /* Query demo 
           * POTENTIAL TESTS:
           *  - remove one square bracket or parenthesis to show compiling error at right place in query
           *  - change Input Args2 to Args3 to show compiling error (beginning of query)
           *  - erase ?a to show compiling error in query (beginning of query)
           */
          query[Args2, Args3]("""
            [ 
              :find ?e ?name ?a
              :in $ ?age
              :where  [ ?e :person/name ?name ] 
                      [ ?e :person/age ?a ]
                      [ (< ?a ?age) ]
            ]
          """).all().execute(database, DLong(40)).map( results =>
              Future.successful(results.map {
                case (id: DLong, name: DString, age: DLong) => 
                  // can get entity there
                  val entity = database.entity(id)
                  println(s"""entity: $id - name $name - characters ${entity.get(":person/character")}""")
                  name -> age
                case e => throw new RuntimeException("unexpected result")
              })
            ).recover{ case e => Future.failed(e) }.get
        }
      }.recover{
        case e => failure(e.getMessage)
      }

      Await.result(
        fut,
        Duration("30 seconds")
      ).toSet must beEqualTo(Set(DString("toto") -> DLong(30L), DString("tutu") -> DLong(35L)))
    }
  }
}