Skip to content

Commit

Permalink
Merge branch 'develop' as v0.6 release
Browse files Browse the repository at this point in the history
  • Loading branch information
dwhjames committed Nov 16, 2013
2 parents ff705c8 + 8404dc3 commit 1a7ae40
Show file tree
Hide file tree
Showing 23 changed files with 1,729 additions and 1,764 deletions.
4 changes: 4 additions & 0 deletions extras/src/main/scala/datomisca/Props.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ sealed trait Props {
private def ::[DD <: DatomicData, Card <: Cardinality, A](prop: (Attribute[DD, Card], A))
(implicit attrC: Attribute2PartialAddEntityWriter[DD, Card, A]): Props = PropsLink(prop, this, attrC)

@deprecated(message = "Use SchemaEntity.newBuilder instead.", since = "0.6")
def +[DD <: DatomicData, Card <: Cardinality, A](prop: (Attribute[DD, Card], A))
(implicit attrC: Attribute2PartialAddEntityWriter[DD, Card, A]) = {
def step(cur: Props): Props = {
Expand All @@ -46,6 +47,7 @@ sealed trait Props {
step(this)
}

@deprecated(message = "Use SchemaEntity.newBuilder instead.", since = "0.6")
def ++(other: Props): Props = {
def step(cur: Props): Props = {
cur match {
Expand All @@ -59,8 +61,10 @@ sealed trait Props {
}

object Props {
@deprecated(message = "Use SchemaEntity.newBuilder instead.", since = "0.6")
def apply() = PropsNil

@deprecated(message = "Use SchemaEntity.newBuilder instead.", since = "0.6")
def apply[DD <: DatomicData, Card <: Cardinality, A](prop: (Attribute[DD, Card], A))
(implicit attrC: Attribute2PartialAddEntityWriter[DD, Card, A]): Props = {
prop :: PropsNil
Expand Down
209 changes: 209 additions & 0 deletions integration/src/it/scala/datomisca/AccountsSampleSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright 2012 Pellucid and Zenexity
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package datomisca

import org.scalatest.{FlatSpec, Matchers}

import scala.concurrent.ExecutionContext.Implicits.global


class AccountsSampleSpec
extends FlatSpec
with Matchers
with DatomicFixture
with AwaitHelper
{

object AccountsSchema {
// Namespaces
val account = new Namespace("account")
val trans = new Namespace("trans")
val fn = new Namespace("fn")

// Attributes
val name = Attribute(account / "name", SchemaType.string, Cardinality.one)
.withDoc("The name of the account")
.withUnique(Unique.value)
.withFullText(true)
val balance = Attribute(account / "balance", SchemaType.bigdec, Cardinality.one)
.withDoc("The account balance")
val minBalance = Attribute(account / "min-balance", SchemaType.bigdec, Cardinality.one)
.withDoc("The minimum permitted balance for the account")

val amount = Attribute(trans / "amount", SchemaType.bigdec, Cardinality.one)
.withDoc("The transaction amount")
val from = Attribute(trans / "from", SchemaType.ref, Cardinality.one)
.withDoc("The sending account")
val to = Attribute(trans / "to", SchemaType.ref, Cardinality.one)
.withDoc("The receiving account")


val creditFn = AddTxFunction.typed[Long, BigDecimal](fn / "credit") ("db", "id", "amount") ("clojure") {s"""
(let [e (d/entity db id)
min-balance (${minBalance} e 0)
balance (+ (${balance} e 0) amount) ]
(if (>= balance min-balance)
[[:db/add id ${balance} balance]]
(throw (Exception. "Insufficient funds"))))
"""}

val transferFn = AddTxFunction.typed[Long, Long, BigDecimal](fn / "transfer") ("db", "from", "to", "amount") ("clojure") {s"""
[[${creditFn.ident} from (- amount)]
[${creditFn.ident} to amount]]
"""}

// Schema
val schema = Seq(
name, balance, minBalance,
amount, from, to,
creditFn, transferFn
)
}


object AccountsTxData {
import AccountsSchema._

val issuer = (
SchemaEntity.newBuilder
+= (name -> "Issuer")
+= (balance -> BigDecimal(0))
+= (minBalance -> BigDecimal(-1000))
) withId DId(Partition.USER)

val bob = (
SchemaEntity.newBuilder
+= (AccountsSchema.name -> "Bob")
+= (balance -> BigDecimal(0))
+= (minBalance -> BigDecimal(0))
) withId DId(Partition.USER)

val alice = (
SchemaEntity.newBuilder
+= (AccountsSchema.name -> "Alice")
+= (balance -> BigDecimal(0))
+= (minBalance -> BigDecimal(0))
) withId DId(Partition.USER)

val sampleTxData = Seq(issuer, bob, alice)

def transfer(fromAcc: Long, toAcc: Long, transAmount: BigDecimal, note: String): Seq[Operation] = {
val txId = DId(Partition.TX)
Seq(
InvokeTxFunction(transferFn)(fromAcc, toAcc, transAmount),
Fact.add(txId)(Attribute.doc -> note),
(SchemaEntity.newBuilder
+= (from -> fromAcc)
+= (to -> toAcc)
+= (amount -> transAmount)
) withId txId
)
}

def credit(toAcc: Long, amount: BigDecimal): Operation =
InvokeTxFunction(creditFn)(toAcc, amount)
}


object AccountsQueries {
import AccountsSchema._

val queryAccounts = Query(s"""
[:find ?a
:in $$
:where [?a ${name}]]
""")

val findAccountByName = Query(s"""
[
:find ?a
:in $$ ?name
:where
[?a ${name} ?name]
]
""")

val queryAllTransactions = Query(s"""
[:find ?tx
:in $$
:where
[?tx ${amount}]]
""")

val rulesParty = Query.rules(s"""
[[[party ?t ?a]
[?t ${from} ?a]]
[[party ?t ?a]
[?t ${to} ?a]]]
""")

val queryAccountTransactions = Query("""
[:find ?t
:in $ % ?a
:where
(party ?t ?a)]
""")
}


"Accounts Sample" should "run to completion" in withDatomicDB { implicit conn =>
import AccountsSchema.{name, from, to}
import AccountsQueries._
import AccountsTxData.{transfer, credit}

await {
Datomic.transact(AccountsSchema.schema)
}
await {
Datomic.transact(AccountsTxData.sampleTxData)
}

val DLong(issuerId) =
Datomic.q(findAccountByName, conn.database, DString("Issuer")).head
val DLong(bobId) =
Datomic.q(findAccountByName, conn.database, DString("Bob")).head
val DLong(aliceId) =
Datomic.q(findAccountByName, conn.database, DString("Alice")).head

await {
Datomic.transact(transfer(issuerId, aliceId, BigDecimal(77), "Issuance to Alice"))
}
await {
Datomic.transact(transfer(issuerId, bobId, BigDecimal(23), "Issuance to Bob"))
}
await {
Datomic.transact(transfer(aliceId, bobId, BigDecimal(7), "Dinner"))
}

Datomic.q(queryAccounts, conn.database) should have size (3)

Datomic.q(queryAllTransactions, conn.database) should have size (3)

Datomic.q(queryAccountTransactions, conn.database, rulesParty, DLong(issuerId)) should have size (2)

Datomic.q(queryAccountTransactions, conn.database, rulesParty, DLong(bobId)) should have size (2)

Datomic.q(queryAccountTransactions, conn.database, rulesParty, DLong(aliceId)) should have size (2)

an [Exception] should be thrownBy {
await {
Datomic.transact(transfer(aliceId, bobId, BigDecimal(71), "Car"))
}
}
}

}
144 changes: 144 additions & 0 deletions integration/src/it/scala/datomisca/AggregatesSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright 2012 Pellucid and Zenexity
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package datomisca

import org.scalatest.{FlatSpec, Matchers, OptionValues}


class AggregatesSpec
extends FlatSpec
with Matchers
with OptionValues
with DatomicFixture
{

val countObjects = Query("""
[:find (count ?e)
:where [?e :object/name ?n]]
""")

val findLargestRadius = Query("""
[:find (max ?radius)
:where [_ :object/meanRadius ?radius]]
""")

val findSmallestRadius = Query("""
[:find (min ?radius)
:where [_ :object/meanRadius ?radius]]
""")

val findAverageRadius = Query("""
[:find (avg ?radius)
:with ?e
:where [?e :object/meanRadius ?radius]]
""")

val findMedianRadius = Query("""
[:find (median ?radius)
:with ?e
:where [?e :object/meanRadius ?radius]]
""")

val findStdDevOfRadius = Query("""
[:find (stddev ?radius)
:with ?e
:where [?e :object/meanRadius ?radius]]
""")

val findRandomObject = Query("""
[:find (rand ?name)
:where [?e :object/name ?name]]
""")

val findSmallest3 = Query("""
[:find (min 3 ?radius)
:with ?e
:where [?e :object/meanRadius ?radius]]
""")

val findLargest3 = Query("""
[:find (max 3 ?radius)
:with ?e
:where [?e :object/meanRadius ?radius]]
""")

val findRandom5 = Query("""
[:find (rand 5 ?name)
:with ?e
:where [?e :object/name ?name]]
""")

val choose5 = Query("""
[:find (sample 5 ?name)
:with ?e
:where [?e :object/name ?name]]
""")

val findAvgSchemaNameLength = Query("""
[:find (avg ?length)
:with ?e
:where
[?e :db/ident ?ident]
[(name ?ident) ?name]
[(count ?name) ?length]]
""")

val countAttributesAndValueTypesInSchema = Query("""
[:find (count ?a) (count-distinct ?vt)
:where
[?a :db/ident ?ident]
[?a :db/valueType ?vt]]
""")

"Aggregates examples" should "run to completion" in withSampleDatomicDB(PlutoSampleData) { conn =>
val db = conn.database

Datomic.q(countObjects, db).headOption.value should equal (DLong(17))

Datomic.q(findLargestRadius, db).headOption.value should equal (DDouble(696000.0))

Datomic.q(findSmallestRadius, db).headOption.value should equal (DDouble(1163.0))

Datomic.q(findAverageRadius, db).headOption.value should equal (DDouble(53390.17647058824))

Datomic.q(findMedianRadius, db).headOption.value should equal (DDouble(2631.2))

Datomic.q(findStdDevOfRadius, db).headOption.value should equal (DDouble(161902.5278094546))

Datomic.q(findRandomObject, db) should have size (1)

Datomic.q(findSmallest3, db).headOption.value match {
case DColl(coll) =>
coll should contain allOf (DDouble(1163.0), DDouble(1353.4), DDouble(1561.0))
case _ => fail
}

Datomic.q(findLargest3, db).headOption.value match {
case DColl(coll) =>
coll should contain allOf (DDouble(696000.0), DDouble(69911.0), DDouble(58232.0))
case _ => fail
}

Datomic.q(findRandom5, db) should have size (1)

Datomic.q(choose5, db) should have size (1)

Datomic.q(findAvgSchemaNameLength, db).headOption.value should equal (DDouble(5.846153846153846))

Datomic.q(countAttributesAndValueTypesInSchema, db) should have size (1)
}
}
Loading

0 comments on commit 1a7ae40

Please sign in to comment.